linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] add ASoC components for AHUB
@ 2020-01-20 14:23 Sameer Pujar
  2020-01-20 14:23 ` [PATCH 1/9] dt-bindings: sound: tegra: add DT binding " Sameer Pujar
                   ` (9 more replies)
  0 siblings, 10 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

Overview
========
The Audio Hub (AHUB) is part of the Audio Processing Engine (APE) which
comprises a collection of hardware accelerators for audio pre-processing
and post-processing. It also includes a programmable full crossbar for
routing audio data across these accelerators.

This series exposes some of these below mentioned HW devices as ASoC
components for Tegra platforms from Tegra210 onwards.
 * ADMAIF : The interface between ADMA and AHUB
 * XBAR   : Crossbar for routing audio samples across various modules
 * I2S    : Inter-IC Sound Controller
 * DMIC   : Digital Microphone
 * DSPK   : Digital Speaker

Following is the summary of current series.
 1. Add YAML DT binding documentation for above mentioned modules.
 2. ACIF programming is same for Tegra generations and hence it is moved
    to a common file.
 3. Add ASoC driver components for each of the above modules.
 4. Add DT entries for above components for Tegra210, Tegra186 and
    Tegra194.
 5. Enable these components for Jetson-Tx1, Jetson-Tx2 and
    Jetson-Xavier.

Machine driver series will be sent separately.

Sameer Pujar (9):
  dt-bindings: sound: tegra: add DT binding for AHUB
  ASoC: tegra: add support for CIF programming
  ASoC: tegra: add Tegra210 based DMIC driver
  ASoC: tegra: add Tegra210 based I2S driver
  ASoC: tegra: add Tegra210 based AHUB driver
  ASoC: tegra: add Tegra186 based DSPK driver
  ASoC: tegra: add Tegra210 based ADMAIF driver
  arm64: tegra: add AHUB components for few Tegra chips
  arm64: tegra: enable AHUB modules for few Tegra chips

 .../bindings/sound/nvidia,tegra186-dspk.yaml       | 105 +++
 .../bindings/sound/nvidia,tegra210-admaif.yaml     | 165 ++++
 .../bindings/sound/nvidia,tegra210-ahub.yaml       | 130 +++
 .../bindings/sound/nvidia,tegra210-dmic.yaml       | 105 +++
 .../bindings/sound/nvidia,tegra210-i2s.yaml        | 112 +++
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts |  48 ++
 arch/arm64/boot/dts/nvidia/tegra186.dtsi           | 231 ++++-
 arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts |  36 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi           | 239 +++++-
 arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts |  40 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi           | 145 ++++
 sound/soc/tegra/Kconfig                            |  56 ++
 sound/soc/tegra/Makefile                           |  12 +
 sound/soc/tegra/tegra186_dspk.c                    | 516 +++++++++++
 sound/soc/tegra/tegra186_dspk.h                    |  73 ++
 sound/soc/tegra/tegra210_admaif.c                  | 896 ++++++++++++++++++++
 sound/soc/tegra/tegra210_admaif.h                  | 164 ++++
 sound/soc/tegra/tegra210_ahub.c                    | 667 +++++++++++++++
 sound/soc/tegra/tegra210_ahub.h                    | 125 +++
 sound/soc/tegra/tegra210_dmic.c                    | 522 ++++++++++++
 sound/soc/tegra/tegra210_dmic.h                    |  85 ++
 sound/soc/tegra/tegra210_i2s.c                     | 941 +++++++++++++++++++++
 sound/soc/tegra/tegra210_i2s.h                     | 132 +++
 sound/soc/tegra/tegra30_ahub.c                     |  94 +-
 sound/soc/tegra/tegra30_ahub.h                     | 129 ---
 sound/soc/tegra/tegra30_i2s.c                      |  35 +-
 sound/soc/tegra/tegra30_i2s.h                      |   7 -
 sound/soc/tegra/tegra_cif.c                        |  34 +
 sound/soc/tegra/tegra_cif.h                        |  50 ++
 sound/soc/tegra/tegra_pcm.c                        | 224 ++++-
 sound/soc/tegra/tegra_pcm.h                        |  23 +-
 31 files changed, 5897 insertions(+), 244 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
 create mode 100644 sound/soc/tegra/tegra186_dspk.c
 create mode 100644 sound/soc/tegra/tegra186_dspk.h
 create mode 100644 sound/soc/tegra/tegra210_admaif.c
 create mode 100644 sound/soc/tegra/tegra210_admaif.h
 create mode 100644 sound/soc/tegra/tegra210_ahub.c
 create mode 100644 sound/soc/tegra/tegra210_ahub.h
 create mode 100644 sound/soc/tegra/tegra210_dmic.c
 create mode 100644 sound/soc/tegra/tegra210_dmic.h
 create mode 100644 sound/soc/tegra/tegra210_i2s.c
 create mode 100644 sound/soc/tegra/tegra210_i2s.h
 create mode 100644 sound/soc/tegra/tegra_cif.c
 create mode 100644 sound/soc/tegra/tegra_cif.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/9] dt-bindings: sound: tegra: add DT binding for AHUB
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-20 14:23 ` [PATCH 2/9] ASoC: tegra: add support for CIF programming Sameer Pujar
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

Audio Hub (AHUB) comprises a collection of hardware accelerators for audio
pre-processing and post-processing and a programmable full crossbar for
audio routing across these accelerators. This patch adds YAML schema for DT
binding of AHUB and few of its following components. These devices will be
registered as ASoC components.
 * ADMAIF
 * I2S
 * DMIC
 * DSPK

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 .../bindings/sound/nvidia,tegra186-dspk.yaml       | 105 +++++++++++++
 .../bindings/sound/nvidia,tegra210-admaif.yaml     | 165 +++++++++++++++++++++
 .../bindings/sound/nvidia,tegra210-ahub.yaml       | 130 ++++++++++++++++
 .../bindings/sound/nvidia,tegra210-dmic.yaml       | 105 +++++++++++++
 .../bindings/sound/nvidia,tegra210-i2s.yaml        | 112 ++++++++++++++
 5 files changed, 617 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml

diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
new file mode 100644
index 0000000..dc9fef3
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra186-dspk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra186 DSPK Controller Device Tree Bindings
+
+description: |
+  The Digital Speaker Controller (DSPK) can be viewed as a Pulse
+  Density Modulation (PDM) transmitter that up-samples the input to
+  the desired sampling rate by interpolation and then converts the
+  over sampled Pulse Code Modulation (PCM) input to the desired 1-bit
+  output via Delta Sigma Modulation (DSM).
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          const: nvidia,tegra186-dspk
+      - items:
+          - enum:
+              - nvidia,tegra194-dspk
+          - enum:
+              - nvidia,tegra186-dspk
+
+  reg:
+    description: offset and length of the register set for the device.
+    maxItems: 1
+
+  clocks:
+    description: must contain an entry for each entry in clock-names.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: Module clock
+
+  clock-names:
+    description: must contain clock names for each corresponding entry in clocks.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - const: dspk
+
+  assigned-clocks:
+    description: list of input clocks and dividers for the audio system.
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: Module clock
+
+  assigned-clock-parents:
+    description: parent clock for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: Parent for module clock
+
+  assigned-clock-rates:
+    description: clock rate for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    items:
+      - description: initial module clock rate
+
+  "#sound-dai-cells":
+    const: 1
+
+  sound-name-prefix:
+    $ref: /schemas/types.yaml#/definitions/string
+    description:
+      Used as prefix for sink/source names of the component. Must be a
+      unique string among multiple instances of the same component.
+      The name can be "DSPK1" or "DSPKx", where x depends on the maximum
+      available instances on a Tegra SoC.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - assigned-clocks
+  - assigned-clock-parents
+  - "#sound-dai-cells"
+
+examples:
+  - |
+    #include<dt-bindings/clock/tegra186-clock.h>
+
+    tegra_dspk1: dspk@2905000 {
+        compatible = "nvidia,tegra186-dspk";
+        reg = <0x2905000 0x100>;
+        clocks = <&bpmp TEGRA186_CLK_DSPK1>;
+        clock-names = "dspk";
+        assigned-clocks = <&bpmp TEGRA186_CLK_DSPK1>;
+        assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+        assigned-clock-rates = <12288000>;
+        #sound-dai-cells = <1>;
+        sound-name-prefix = "DSPK1";
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
new file mode 100644
index 0000000..a8a41ba
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
@@ -0,0 +1,165 @@
+# SPAT-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra210-admaif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra210 ADMAIF Device Tree Bindings
+
+description: |
+  ADMAIF is the interface between ADMA and AHUB. Each ADMA channel
+  that sends/receives data to/from AHUB must interface through an
+  ADMAIF channel. ADMA channel sending data to AHUB pairs with ADMAIF
+  Tx channel and ADMA channel receiving data from AHUB pairs with
+  ADMAIF Rx channel.
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          const: nvidia,tegra210-admaif
+      - items:
+          const: nvidia,tegra186-admaif
+      - items:
+          - enum:
+             - nvidia,tegra194-admaif
+          - enum:
+             - nvidia,tegra186-admaif
+
+  reg:
+    maxItems: 1
+
+  dmas: true
+
+  dma-names: true
+
+  "#sound-dai-cells":
+    const: 1
+
+if:
+  properties:
+    compatible:
+      contains:
+        const: nvidia,tegra210-admaif
+
+then:
+  properties:
+    dmas:
+      $ref: /schemas/types.yaml#/definitions/phandle-array
+      description:
+        Two or more DMA channel specifiers, equally divided for Tx and Rx.
+      minItems: 2
+      maxItems: 20
+    dma-names:
+      $ref: /schemas/types.yaml#/definitions/string-array
+      description:
+        There must be at least one channel named "tx1" for transmit and "rx1"
+        for receive. If more channels need to be specified then "tx1",
+        "tx2" ... "tx10" can be used. Similarly for Rx channels as well.
+      minItems: 2
+      maxItems: 20
+
+else:
+  properties:
+    dmas:
+      $ref: /schemas/types.yaml#/definitions/phandle-array
+      description:
+        Two or more DMA channel specifiers, equally divided for Tx and Rx.
+      minItems: 2
+      maxItems: 40
+    dma-names:
+      $ref: /schemas/types.yaml#/definitions/string-array
+      description:
+        There must be at least one channel named "tx1" for transmit and "x1"
+        for receive. If more channels need to be specified then "tx1",
+        "tx2" ... "tx20" can be used. Similarly for Rx channels as well.
+      minItems: 2
+      maxItems: 40
+
+required:
+  - compatible
+  - reg
+  - dmas
+  - dma-names
+  - "#sound-dai-cells"
+
+examples:
+  - |
+    admaif@702d0000 {
+        compatible = "nvidia,tegra210-admaif";
+        reg = <0x702d0000 0x800>;
+        dmas = <&adma 1>,  <&adma 1>,
+               <&adma 2>,  <&adma 2>,
+               <&adma 3>,  <&adma 3>,
+               <&adma 4>,  <&adma 4>,
+               <&adma 5>,  <&adma 5>,
+               <&adma 6>,  <&adma 6>,
+               <&adma 7>,  <&adma 7>,
+               <&adma 8>,  <&adma 8>,
+               <&adma 9>,  <&adma 9>,
+               <&adma 10>, <&adma 10>;
+        dma-names = "rx1",  "tx1",
+                    "rx2",  "tx2",
+                    "rx3",  "tx3",
+                    "rx4",  "tx4",
+                    "rx5",  "tx5",
+                    "rx6",  "tx6",
+                    "rx7",  "tx7",
+                    "rx8",  "tx8",
+                    "rx9",  "tx9",
+                    "rx10", "tx10";
+        #sound-dai-cells = <1>;
+    };
+
+  - |
+    admaif@290f000 {
+        compatible = "nvidia,tegra186-admaif";
+        reg = <0x0290f000 0x1000>;
+        dmas = <&adma 1>, <&adma 1>,
+               <&adma 2>, <&adma 2>,
+               <&adma 3>, <&adma 3>,
+               <&adma 4>, <&adma 4>,
+               <&adma 5>, <&adma 5>,
+               <&adma 6>, <&adma 6>,
+               <&adma 7>, <&adma 7>,
+               <&adma 8>, <&adma 8>,
+               <&adma 9>, <&adma 9>,
+               <&adma 10>, <&adma 10>,
+               <&adma 11>, <&adma 11>,
+               <&adma 12>, <&adma 12>,
+               <&adma 13>, <&adma 13>,
+               <&adma 14>, <&adma 14>,
+               <&adma 15>, <&adma 15>,
+               <&adma 16>, <&adma 16>,
+               <&adma 17>, <&adma 17>,
+               <&adma 18>, <&adma 18>,
+               <&adma 19>, <&adma 19>,
+               <&adma 20>, <&adma 20>;
+        dma-names = "rx1", "tx1",
+                    "rx2", "tx2",
+                    "rx3", "tx3",
+                    "rx4", "tx4",
+                    "rx5", "tx5",
+                    "rx6", "tx6",
+                    "rx7", "tx7",
+                    "rx8", "tx8",
+                    "rx9", "tx9",
+                    "rx10", "tx10",
+                    "rx11", "tx11",
+                    "rx12", "tx12",
+                    "rx13", "tx13",
+                    "rx14", "tx14",
+                    "rx15", "tx15",
+                    "rx16", "tx16",
+                    "rx17", "tx17",
+                    "rx18", "tx18",
+                    "rx19", "tx19",
+                    "rx20", "tx20";
+        #sound-dai-cells = <1>;
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
new file mode 100644
index 0000000..5221d01
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra210-ahub.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra210 AHUB Device Tree Bindings
+
+description: |
+  The Audio Hub (AHUB) comprises a collection of hardware accelerators
+  for audio pre-processing, post-processing and a programmable full
+  crossbar for routing audio data across these accelerators. It has
+  external interfaces such as I2S, DMIC, DSPK. It interfaces with ADMA
+  engine through ADMAIF.
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - const: nvidia,tegra210-ahub
+      - items:
+          - const: nvidia,tegra186-ahub
+      - items:
+          - enum:
+              - nvidia,tegra194-ahub
+          - enum:
+              - nvidia,tegra186-ahub
+
+  reg:
+    description: offset and length of the register set for the device.
+    maxItems: 1
+
+  clocks:
+    description: must contain an entry for each entry in clock-names.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: AHUB module clock
+
+  clock-names:
+    description: must contain clock names for each corresponding entry in clocks.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - const: ahub
+
+  assigned-clocks:
+    description: list of input clocks and dividers for the audio system.
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: AHUB module clock
+
+  assigned-clock-parents:
+    description: parent clock for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: parent for AHUB module clock
+
+  assigned-clock-rates:
+    description: clock rate for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    items:
+      - description: AHUB module clock rate
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 1
+
+  ranges:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    description: Specify address range for the node and its children
+    minItems: 1
+
+  "#sound-dai-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - assigned-clocks
+  - assigned-clock-parents
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - "#sound-dai-cells"
+
+examples:
+  - |
+    #include<dt-bindings/clock/tegra210-car.h>
+
+    ahub@702d0800 {
+        compatible = "nvidia,tegra210-ahub";
+        reg = <0x702d0800 0x800>;
+        clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+        clock-names = "ahub";
+        assigned-clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+        assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x702d0000 0x702d0000 0x0000e400>;
+        #sound-dai-cells = <1>;
+    };
+
+  - |
+    #include<dt-bindings/clock/tegra186-clock.h>
+
+    ahub@2900800 {
+        compatible = "nvidia,tegra186-ahub";
+        reg = <0x02900800 0x800>;
+        clocks = <&bpmp TEGRA186_CLK_AHUB>;
+        clock-names = "ahub";
+        assigned-clocks = <&bpmp TEGRA186_CLK_AHUB>;
+        assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x02900800 0x02900800 0x11800>;
+        #sound-dai-cells = <1>;
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
new file mode 100644
index 0000000..dc02ce4
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra210-dmic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra210 DMIC Controller Device Tree Bindings
+
+description: |
+  The Digital MIC (DMIC) Controller is used to interface with Pulse
+  Density Modulation (PDM) input devices. It converts PDM signals to
+  Pulse Coded Modulation (PCM) signals. DMIC can be viewed as a PDM
+  receiver.
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          const: nvidia,tegra210-dmic
+      - items:
+          - enum:
+              - nvidia,tegra194-dmic
+              - nvidia,tegra186-dmic
+          - enum:
+              - nvidia,tegra210-dmic
+
+  reg:
+    description: offset and length of the register set for the device.
+    maxItems: 1
+
+  clocks:
+    description: must contain an entry for each entry in clock-names.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: DMIC interface clock (also goes to codec).
+
+  clock-names:
+    description: must contain clock names for each corresponding entry in clocks.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - const: dmic
+
+  assigned-clocks:
+    description: list of input clocks and dividers for the audio system.
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: DMIC interface clock.
+
+  assigned-clock-parents:
+    description: parent clock for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: parent for DMIC interface clock.
+
+  assigned-clock-rates:
+    description: clock rate for each entry in assigned-clocks
+      See ../clocks/clock-bindings.txt for details.
+    items:
+      - description: rate for DMIC interface clock.
+
+  "#sound-dai-cells":
+    const: 1
+
+  sound-name-prefix:
+    $ref: /schemas/types.yaml#/definitions/string
+    description:
+      used as prefix for sink/source names of the component. Must be a
+      unique string among multiple instances of the same component.
+      The name can be "DMIC1" or "DMIC2" ... "DMICx", where x depends
+      on the maximum available instances on a Tegra SoC.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - assigned-clocks
+  - assigned-clock-parents
+  - "#sound-dai-cells"
+
+examples:
+  - |
+    #include<dt-bindings/clock/tegra210-car.h>
+
+    tegra_dmic1: dmic@702d4000 {
+        compatible = "nvidia,tegra210-dmic";
+        reg = <0x702d4000 0x100>;
+        clocks = <&tegra_car TEGRA210_CLK_DMIC1>;
+        clock-names = "dmic";
+        assigned-clocks = <&tegra_car TEGRA210_CLK_DMIC1>;
+        assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+        assigned-clock-rates = <3072000>;
+        #sound-dai-cells = <1>;
+        sound-name-prefix = "DMIC1";
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
new file mode 100644
index 0000000..f6f2f2d
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nvidia,tegra210-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Tegra210 I2S Controller Device Tree Bindings
+
+description: |
+  The Inter-IC Sound (I2S) controller implements full-duplex,
+  bi-directional and single direction point-to-point serial
+  interfaces. It can interface with I2S compatible devices.
+  I2S controller can operate both in master and slave mode.
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Sameer Pujar <spujar@nvidia.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          const: nvidia,tegra210-i2s
+      - items:
+          - enum:
+              - nvidia,tegra194-i2s
+              - nvidia,tegra186-i2s
+          - enum:
+              - nvidia,tegra210-i2s
+
+  reg:
+    description: offset and length of the register set for the device.
+    maxItems: 1
+
+  clocks:
+    description: must contain an entry for each entry in clock-names.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: I2S bit clock
+      - description:
+          Sync input clock, which can act as clock source to other I/O
+          modules in AHUB. The Tegra I2S driver sets this clock rate as
+          per bit clock rate. I/O module which wants to use this clock
+          as source, can mention this clock as parent in the DT bindings.
+          This is an optional clock entry, since it is only required when
+          some other I/O wants to reference from a particular I2Sx
+          instance.
+
+  clock-names:
+    description: must contain clock names for each corresponding entry in clocks.
+      See ../clocks/clocks-bindings.txt for details.
+    minItems: 1
+    items:
+      - const: i2s
+      - const: sync_input
+
+  assigned-clocks:
+    description: list of input clocks and dividers for the audio system.
+      See ../clocks/clock-bindings.txt for details.
+    minItems: 1
+    items:
+      - description: Bit clock
+
+  assigned-clock-parents:
+    description: parent clock for each entry in assigned-clocks
+    minItems: 1
+    items:
+      - description: Parent for bit clock
+
+  assigned-clock-rates:
+    description: clock rate for each entry in assigned-clocks
+    items:
+      - description: Initial bit clock rate
+
+  "#sound-dai-cells":
+    const: 1
+
+  sound-name-prefix:
+    $ref: /schemas/types.yaml#/definitions/string
+    description:
+      Used as prefix for sink/source names of the component. Must be a
+      unique string among multiple instances of the same component.
+      The name can be "I2S1" or "I2S2" ... "I2Sx", where x depends
+      on the maximum available instances on a Tegra SoC.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - assigned-clocks
+  - assigned-clock-parents
+  - "#sound-dai-cells"
+
+examples:
+  - |
+    #include<dt-bindings/clock/tegra210-car.h>
+
+    tegra_i2s1: i2s@702d1000 {
+        compatible = "nvidia,tegra210-i2s";
+        reg = <0x702d1000 0x100>;
+        clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+        clock-names = "i2s";
+        assigned-clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+        assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+        assigned-clock-rates = <1536000>;
+        #sound-dai-cells = <1>;
+        sound-name-prefix = "I2S1";
+    };
+
+...
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
  2020-01-20 14:23 ` [PATCH 1/9] dt-bindings: sound: tegra: add DT binding " Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-20 15:58   ` [alsa-devel] " Dmitry Osipenko
  2020-01-20 14:23 ` [PATCH 3/9] ASoC: tegra: add Tegra210 based DMIC driver Sameer Pujar
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

Audio Client Interface (CIF) is a proprietary interface employed to route
audio samples through Audio Hub (AHUB) components by inter connecting the
various modules. This patch exposes a helper function to program CIF
instance present in various modules. Update Makefile to build tegra_cif.c
and build dependency is added over config CONFIG_SND_SOC_TEGRA.

Tegra30 and Tegra124 have an identical CIF programming helper function.
Remove tegra30_ahub_set_cif(), tegra124_ahub_set_cif() and instead we can
rely on common exposed function tegra_set_cif(). Tegra30 and Tegra124 I2S
driver uses soc_data to reference different versions of set_audio_cif
function via callback function pointer. This is no more required with
current patch and hence remove soc_data from i2s driver.

Also update variable names for audio and client channels. This is required
to avoid wrapping in function tegra_set_cif().

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/Makefile       |   2 +
 sound/soc/tegra/tegra30_ahub.c |  94 +++++-------------------------
 sound/soc/tegra/tegra30_ahub.h | 129 -----------------------------------------
 sound/soc/tegra/tegra30_i2s.c  |  35 ++++-------
 sound/soc/tegra/tegra30_i2s.h  |   7 ---
 sound/soc/tegra/tegra_cif.c    |  34 +++++++++++
 sound/soc/tegra/tegra_cif.h    |  50 ++++++++++++++++
 7 files changed, 111 insertions(+), 240 deletions(-)
 create mode 100644 sound/soc/tegra/tegra_cif.c
 create mode 100644 sound/soc/tegra/tegra_cif.h

diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index c84f183..261aa21 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -8,9 +8,11 @@ snd-soc-tegra20-i2s-objs := tegra20_i2s.o
 snd-soc-tegra20-spdif-objs := tegra20_spdif.o
 snd-soc-tegra30-ahub-objs := tegra30_ahub.o
 snd-soc-tegra30-i2s-objs := tegra30_i2s.o
+snd-soc-tegra-cif-objs := tegra_cif.o
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-cif.o
 obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
 obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
 obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 635eacb..8f95cff 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <sound/soc.h>
 #include "tegra30_ahub.h"
+#include "tegra_cif.h"
 
 #define DRV_NAME "tegra30-ahub"
 
@@ -90,7 +91,7 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
 {
 	int channel;
 	u32 reg, val;
-	struct tegra30_ahub_cif_conf cif_conf;
+	struct tegra_cif_conf cif_conf;
 
 	channel = find_first_zero_bit(ahub->rx_usage,
 				      TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
@@ -117,20 +118,19 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
 	tegra30_apbif_write(reg, val);
 
 	cif_conf.threshold = 0;
-	cif_conf.audio_channels = 2;
-	cif_conf.client_channels = 2;
-	cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
-	cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
+	cif_conf.audio_ch = 2;
+	cif_conf.client_ch = 2;
+	cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+	cif_conf.client_bits = TEGRA_ACIF_BITS_16;
 	cif_conf.expand = 0;
 	cif_conf.stereo_conv = 0;
 	cif_conf.replicate = 0;
-	cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
 	cif_conf.truncate = 0;
 	cif_conf.mono_conv = 0;
 
 	reg = TEGRA30_AHUB_CIF_RX_CTRL +
 	      (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
-	ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf);
+	tegra_set_cif(ahub->regmap_apbif, reg, &cif_conf);
 
 	pm_runtime_put(ahub->dev);
 
@@ -192,7 +192,7 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
 {
 	int channel;
 	u32 reg, val;
-	struct tegra30_ahub_cif_conf cif_conf;
+	struct tegra_cif_conf cif_conf;
 
 	channel = find_first_zero_bit(ahub->tx_usage,
 				      TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
@@ -219,20 +219,19 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
 	tegra30_apbif_write(reg, val);
 
 	cif_conf.threshold = 0;
-	cif_conf.audio_channels = 2;
-	cif_conf.client_channels = 2;
-	cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
-	cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
+	cif_conf.audio_ch = 2;
+	cif_conf.client_ch = 2;
+	cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+	cif_conf.client_bits = TEGRA_ACIF_BITS_16;
 	cif_conf.expand = 0;
 	cif_conf.stereo_conv = 0;
 	cif_conf.replicate = 0;
-	cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
 	cif_conf.truncate = 0;
 	cif_conf.mono_conv = 0;
 
 	reg = TEGRA30_AHUB_CIF_TX_CTRL +
 	      (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
-	ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf);
+	tegra_set_cif(ahub->regmap_apbif, reg, &cif_conf);
 
 	pm_runtime_put(ahub->dev);
 
@@ -485,17 +484,14 @@ static const struct regmap_config tegra30_ahub_ahub_regmap_config = {
 
 static struct tegra30_ahub_soc_data soc_data_tegra30 = {
 	.mod_list_mask = MOD_LIST_MASK_TEGRA30,
-	.set_audio_cif = tegra30_ahub_set_cif,
 };
 
 static struct tegra30_ahub_soc_data soc_data_tegra114 = {
 	.mod_list_mask = MOD_LIST_MASK_TEGRA114,
-	.set_audio_cif = tegra30_ahub_set_cif,
 };
 
 static struct tegra30_ahub_soc_data soc_data_tegra124 = {
 	.mod_list_mask = MOD_LIST_MASK_TEGRA124,
-	.set_audio_cif = tegra124_ahub_set_cif,
 };
 
 static const struct of_device_id tegra30_ahub_of_match[] = {
@@ -670,70 +666,6 @@ static struct platform_driver tegra30_ahub_driver = {
 };
 module_platform_driver(tegra30_ahub_driver);
 
-void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg,
-			  struct tegra30_ahub_cif_conf *conf)
-{
-	unsigned int value;
-
-	value = (conf->threshold <<
-			TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
-		((conf->audio_channels - 1) <<
-			TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
-		((conf->client_channels - 1) <<
-			TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
-		(conf->audio_bits <<
-			TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
-		(conf->client_bits <<
-			TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
-		(conf->expand <<
-			TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
-		(conf->stereo_conv <<
-			TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
-		(conf->replicate <<
-			TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
-		(conf->direction <<
-			TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
-		(conf->truncate <<
-			TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
-		(conf->mono_conv <<
-			TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
-
-	regmap_write(regmap, reg, value);
-}
-EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif);
-
-void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg,
-			   struct tegra30_ahub_cif_conf *conf)
-{
-	unsigned int value;
-
-	value = (conf->threshold <<
-			TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
-		((conf->audio_channels - 1) <<
-			TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
-		((conf->client_channels - 1) <<
-			TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
-		(conf->audio_bits <<
-			TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
-		(conf->client_bits <<
-			TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
-		(conf->expand <<
-			TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
-		(conf->stereo_conv <<
-			TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
-		(conf->replicate <<
-			TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
-		(conf->direction <<
-			TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
-		(conf->truncate <<
-			TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
-		(conf->mono_conv <<
-			TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
-
-	regmap_write(regmap, reg, value);
-}
-EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif);
-
 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 MODULE_DESCRIPTION("Tegra30 AHUB driver");
 MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
index 6889c5f..990d884 100644
--- a/sound/soc/tegra/tegra30_ahub.h
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -8,113 +8,6 @@
 #ifndef __TEGRA30_AHUB_H__
 #define __TEGRA30_AHUB_H__
 
-/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
-
-#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT	28
-#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US	0xf
-#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK	(TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
-
-#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT	24
-#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US	0x3f
-#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK	(TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
-
-/* Channel count minus 1 */
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT	24
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US	7
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
-
-/* Channel count minus 1 */
-#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT	20
-#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US	0xf
-#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK	(TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
-
-/* Channel count minus 1 */
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT	16
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US	7
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
-
-/* Channel count minus 1 */
-#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT	16
-#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US	0xf
-#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
-
-#define TEGRA30_AUDIOCIF_BITS_4				0
-#define TEGRA30_AUDIOCIF_BITS_8				1
-#define TEGRA30_AUDIOCIF_BITS_12			2
-#define TEGRA30_AUDIOCIF_BITS_16			3
-#define TEGRA30_AUDIOCIF_BITS_20			4
-#define TEGRA30_AUDIOCIF_BITS_24			5
-#define TEGRA30_AUDIOCIF_BITS_28			6
-#define TEGRA30_AUDIOCIF_BITS_32			7
-
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT		12
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK		(7                        << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4		(TEGRA30_AUDIOCIF_BITS_4  << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8		(TEGRA30_AUDIOCIF_BITS_8  << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12		(TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16		(TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20		(TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24		(TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28		(TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32		(TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
-
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT		8
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK		(7                        << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4		(TEGRA30_AUDIOCIF_BITS_4  << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8		(TEGRA30_AUDIOCIF_BITS_8  << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12		(TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16		(TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20		(TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24		(TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28		(TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32		(TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
-
-#define TEGRA30_AUDIOCIF_EXPAND_ZERO			0
-#define TEGRA30_AUDIOCIF_EXPAND_ONE			1
-#define TEGRA30_AUDIOCIF_EXPAND_LFSR			2
-
-#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT		6
-#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK		(3                            << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO		(TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE		(TEGRA30_AUDIOCIF_EXPAND_ONE  << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR		(TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
-
-#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0		0
-#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1		1
-#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG		2
-
-#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT		4
-#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK		(3                                << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0		(TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1		(TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG		(TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
-
-#define TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT		3
-
-#define TEGRA30_AUDIOCIF_DIRECTION_TX			0
-#define TEGRA30_AUDIOCIF_DIRECTION_RX			1
-
-#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT		2
-#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK		(1                             << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX		(TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX		(TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
-
-#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND			0
-#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP			1
-
-#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT		1
-#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK		(1                               << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND		(TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP		(TEGRA30_AUDIOCIF_TRUNCATE_CHOP  << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
-
-#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO			0
-#define TEGRA30_AUDIOCIF_MONO_CONV_COPY			1
-
-#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT		0
-#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK		(1                               << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO		(TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
-#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY		(TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
-
 /* Registers within TEGRA30_AUDIO_CLUSTER_BASE */
 
 /* TEGRA30_AHUB_CHANNEL_CTRL */
@@ -471,30 +364,8 @@ extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
 					  enum tegra30_ahub_txcif txcif);
 extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
 
-struct tegra30_ahub_cif_conf {
-	unsigned int threshold;
-	unsigned int audio_channels;
-	unsigned int client_channels;
-	unsigned int audio_bits;
-	unsigned int client_bits;
-	unsigned int expand;
-	unsigned int stereo_conv;
-	unsigned int replicate;
-	unsigned int direction;
-	unsigned int truncate;
-	unsigned int mono_conv;
-};
-
-void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg,
-			  struct tegra30_ahub_cif_conf *conf);
-void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg,
-			   struct tegra30_ahub_cif_conf *conf);
-
 struct tegra30_ahub_soc_data {
 	u32 mod_list_mask;
-	void (*set_audio_cif)(struct regmap *regmap,
-			      unsigned int reg,
-			      struct tegra30_ahub_cif_conf *conf);
 	/*
 	 * FIXME: There are many more differences in HW, such as:
 	 * - More APBIF channels.
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index dbed3c5..36d7f4f 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -32,6 +32,7 @@
 
 #include "tegra30_ahub.h"
 #include "tegra30_i2s.h"
+#include "tegra_cif.h"
 
 #define DRV_NAME "tegra30-i2s"
 
@@ -128,7 +129,7 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
 	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 	unsigned int mask, val, reg;
 	int ret, sample_size, srate, i2sclock, bitcnt, audio_bits;
-	struct tegra30_ahub_cif_conf cif_conf;
+	struct tegra_cif_conf cif_conf;
 
 	if (params_channels(params) != 2)
 		return -EINVAL;
@@ -137,17 +138,17 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
 		val = TEGRA30_I2S_CTRL_BIT_SIZE_16;
-		audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+		audio_bits = TEGRA_ACIF_BITS_16;
 		sample_size = 16;
 		break;
 	case SNDRV_PCM_FORMAT_S24_LE:
 		val = TEGRA30_I2S_CTRL_BIT_SIZE_24;
-		audio_bits = TEGRA30_AUDIOCIF_BITS_24;
+		audio_bits = TEGRA_ACIF_BITS_24;
 		sample_size = 24;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
 		val = TEGRA30_I2S_CTRL_BIT_SIZE_32;
-		audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+		audio_bits = TEGRA_ACIF_BITS_32;
 		sample_size = 32;
 		break;
 	default:
@@ -179,8 +180,8 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
 	regmap_write(i2s->regmap, TEGRA30_I2S_TIMING, val);
 
 	cif_conf.threshold = 0;
-	cif_conf.audio_channels = 2;
-	cif_conf.client_channels = 2;
+	cif_conf.audio_ch = 2;
+	cif_conf.client_ch = 2;
 	cif_conf.audio_bits = audio_bits;
 	cif_conf.client_bits = audio_bits;
 	cif_conf.expand = 0;
@@ -189,15 +190,12 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
 	cif_conf.truncate = 0;
 	cif_conf.mono_conv = 0;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		reg = TEGRA30_I2S_CIF_RX_CTRL;
-	} else {
-		cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
+	else
 		reg = TEGRA30_I2S_CIF_TX_CTRL;
-	}
 
-	i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf);
+	tegra_set_cif(i2s->regmap, reg, &cif_conf);
 
 	val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
 	      (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
@@ -393,17 +391,9 @@ static const struct regmap_config tegra30_i2s_regmap_config = {
 	.cache_type = REGCACHE_FLAT,
 };
 
-static const struct tegra30_i2s_soc_data tegra30_i2s_config = {
-	.set_audio_cif = tegra30_ahub_set_cif,
-};
-
-static const struct tegra30_i2s_soc_data tegra124_i2s_config = {
-	.set_audio_cif = tegra124_ahub_set_cif,
-};
-
 static const struct of_device_id tegra30_i2s_of_match[] = {
-	{ .compatible = "nvidia,tegra124-i2s", .data = &tegra124_i2s_config },
-	{ .compatible = "nvidia,tegra30-i2s", .data = &tegra30_i2s_config },
+	{ .compatible = "nvidia,tegra124-i2s", },
+	{ .compatible = "nvidia,tegra30-i2s", },
 	{},
 };
 
@@ -428,7 +418,6 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
 		ret = -ENODEV;
 		goto err;
 	}
-	i2s->soc_data = (struct tegra30_i2s_soc_data *)match->data;
 
 	i2s->dai = tegra30_i2s_dai_template;
 	i2s->dai.name = dev_name(&pdev->dev);
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index 0b1f312..e2f0d3f 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -214,14 +214,7 @@
 #define TEGRA30_I2S_LCOEF_COEF_MASK_US			0xffff
 #define TEGRA30_I2S_LCOEF_COEF_MASK			(TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT)
 
-struct tegra30_i2s_soc_data {
-	void (*set_audio_cif)(struct regmap *regmap,
-			      unsigned int reg,
-			      struct tegra30_ahub_cif_conf *conf);
-};
-
 struct tegra30_i2s {
-	const struct tegra30_i2s_soc_data *soc_data;
 	struct snd_soc_dai_driver dai;
 	int cif_id;
 	struct clk *clk_i2s;
diff --git a/sound/soc/tegra/tegra_cif.c b/sound/soc/tegra/tegra_cif.c
new file mode 100644
index 0000000..d9f288c
--- /dev/null
+++ b/sound/soc/tegra/tegra_cif.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tegra_cif.c - Tegra Audio CIF Programming for AHUB modules
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include "tegra_cif.h"
+
+void tegra_set_cif(struct regmap *regmap, unsigned int reg,
+		   struct tegra_cif_conf *conf)
+{
+	unsigned int value;
+
+	value = (conf->threshold << TEGRA_ACIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+		((conf->audio_ch - 1) << TEGRA_ACIF_CTRL_AUDIO_CH_SHIFT) |
+		((conf->client_ch - 1) << TEGRA_ACIF_CTRL_CLIENT_CH_SHIFT) |
+		(conf->audio_bits << TEGRA_ACIF_CTRL_AUDIO_BITS_SHIFT) |
+		(conf->client_bits << TEGRA_ACIF_CTRL_CLIENT_BITS_SHIFT) |
+		(conf->expand << TEGRA_ACIF_CTRL_EXPAND_SHIFT) |
+		(conf->stereo_conv << TEGRA_ACIF_CTRL_STEREO_CONV_SHIFT) |
+		(conf->replicate << TEGRA_ACIF_CTRL_REPLICATE_SHIFT) |
+		(conf->truncate << TEGRA_ACIF_CTRL_TRUNCATE_SHIFT) |
+		(conf->mono_conv << TEGRA_ACIF_CTRL_MONO_CONV_SHIFT);
+
+	regmap_update_bits(regmap, reg, TEGRA_ACIF_UPDATE_MASK, value);
+}
+EXPORT_SYMBOL_GPL(tegra_set_cif);
+
+MODULE_DESCRIPTION("Tegra Audio Client Interface (ACIF) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/tegra/tegra_cif.h b/sound/soc/tegra/tegra_cif.h
new file mode 100644
index 0000000..589e947
--- /dev/null
+++ b/sound/soc/tegra/tegra_cif.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra_cif.h - TEGRA Audio CIF Programming
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA_CIF_H__
+#define __TEGRA_CIF_H__
+
+/* Fields in CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
+#define TEGRA_ACIF_CTRL_FIFO_THRESHOLD_SHIFT		24
+/* Channel count minus 1 */
+#define TEGRA_ACIF_CTRL_AUDIO_CH_SHIFT			20
+/* Channel count minus 1 */
+#define TEGRA_ACIF_CTRL_CLIENT_CH_SHIFT			16
+
+#define TEGRA_ACIF_BITS_8				1
+#define TEGRA_ACIF_BITS_16				3
+#define TEGRA_ACIF_BITS_24				5
+#define TEGRA_ACIF_BITS_32				7
+
+#define TEGRA_ACIF_CTRL_AUDIO_BITS_SHIFT		12
+#define TEGRA_ACIF_CTRL_CLIENT_BITS_SHIFT		8
+#define TEGRA_ACIF_CTRL_EXPAND_SHIFT			6
+#define TEGRA_ACIF_CTRL_STEREO_CONV_SHIFT		4
+#define TEGRA_ACIF_CTRL_REPLICATE_SHIFT			3
+#define TEGRA_ACIF_CTRL_TRUNCATE_SHIFT			1
+#define TEGRA_ACIF_CTRL_MONO_CONV_SHIFT			0
+
+#define TEGRA_ACIF_UPDATE_MASK				0x3ffffffb
+
+struct tegra_cif_conf {
+	unsigned int threshold;
+	unsigned int audio_ch;
+	unsigned int client_ch;
+	unsigned int audio_bits;
+	unsigned int client_bits;
+	unsigned int expand;
+	unsigned int stereo_conv;
+	unsigned int replicate;
+	unsigned int truncate;
+	unsigned int mono_conv;
+};
+
+void tegra_set_cif(struct regmap *regmap, unsigned int reg,
+		   struct tegra_cif_conf *conf);
+
+#endif
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 3/9] ASoC: tegra: add Tegra210 based DMIC driver
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
  2020-01-20 14:23 ` [PATCH 1/9] dt-bindings: sound: tegra: add DT binding " Sameer Pujar
  2020-01-20 14:23 ` [PATCH 2/9] ASoC: tegra: add support for CIF programming Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-20 14:23 ` [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver Sameer Pujar
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

The Digital MIC (DMIC) Controller is used to interface with Pulse Density
Modulation (PDM) input devices. The DMIC controller implements a converter
to convert PDM signals to Pulse Code Modulation (PCM) signals. From signal
flow perspective, the DMIC can be viewed as a PDM receiver.

This patch registers DMIC component with ASoC framework. The component
driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
driver exposes DMIC 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 DMIC devices can be enabled in the DT via
"nvidia,tegra210-dmic" compatible string. This driver can be used for
Tegra186 and Tegra194 chips as well.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/Kconfig         |  11 +
 sound/soc/tegra/Makefile        |   2 +
 sound/soc/tegra/tegra210_dmic.c | 522 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra210_dmic.h |  85 +++++++
 4 files changed, 620 insertions(+)
 create mode 100644 sound/soc/tegra/tegra210_dmic.c
 create mode 100644 sound/soc/tegra/tegra210_dmic.h

diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index addadc8..2bde1e6 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -62,6 +62,17 @@ config SND_SOC_TEGRA30_I2S
 	  Tegra30 I2S interface. You will also need to select the individual
 	  machine drivers to support below.
 
+config SND_SOC_TEGRA210_DMIC
+        tristate "Tegra210 DMIC module"
+        depends on SND_SOC_TEGRA
+        help
+          Config to enable the Digital MIC (DMIC) controller which is used
+          to interface with Pulse Density Modulation (PDM) input devices.
+          The DMIC controller implements a converter to convert PDM signals
+          to Pulse Code Modulation (PCM) signals. This can be viewed as a
+          PDM receiver.
+          Say Y or M if you want to add support for Tegra210 DMIC 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 261aa21..9e78fe4 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -9,6 +9,7 @@ snd-soc-tegra20-spdif-objs := tegra20_spdif.o
 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
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
 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
 
 # Tegra machine Support
 snd-soc-tegra-rt5640-objs := tegra_rt5640.o
diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c
new file mode 100644
index 0000000..964f687
--- /dev/null
+++ b/sound/soc/tegra/tegra210_dmic.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tegra210_dmic.c - Tegra210 DMIC 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_dmic.h"
+#include "tegra_cif.h"
+
+#define DRV_NAME "tegra210-dmic"
+
+static const struct reg_default tegra210_dmic_reg_defaults[] = {
+	{ TEGRA210_DMIC_TX_INT_MASK, 0x00000001},
+	{ TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700},
+	{ TEGRA210_DMIC_CG, 0x1},
+	{ TEGRA210_DMIC_CTRL, 0x00000301},
+	/* Below enables all filters - DCR, LP and SC */
+	{ TEGRA210_DMIC_DBG_CTRL, 0xe },
+	/* Below as per latest POR value */
+	{ TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0},
+	/* LP filter is configured for pass through and used to apply gain */
+	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000},
+	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000},
+	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0},
+	{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0},
+};
+
+static int tegra210_dmic_runtime_suspend(struct device *dev)
+{
+	struct tegra210_dmic *dmic = dev_get_drvdata(dev);
+
+	regcache_cache_only(dmic->regmap, true);
+	regcache_mark_dirty(dmic->regmap);
+
+	clk_disable_unprepare(dmic->clk_dmic);
+
+	return 0;
+}
+
+static int tegra210_dmic_runtime_resume(struct device *dev)
+{
+	struct tegra210_dmic *dmic = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(dmic->clk_dmic);
+	if (ret) {
+		dev_err(dev, "failed to enable DMIC clock, err: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(dmic->regmap, false);
+	regcache_sync(dmic->regmap);
+
+	return 0;
+}
+
+static const int tegra210_dmic_fmts[] = {
+	0,
+	TEGRA_ACIF_BITS_16,
+	TEGRA_ACIF_BITS_32,
+};
+
+static int tegra210_dmic_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+	unsigned int srate, clk_rate, channels;
+	struct tegra_cif_conf cif_conf;
+	unsigned long long gain_q23 = DEFAULT_GAIN_Q23;
+	int ret;
+
+	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+	channels = params_channels(params);
+
+	cif_conf.audio_ch = channels;
+	if (dmic->audio_ch_override)
+		cif_conf.audio_ch = dmic->audio_ch_override;
+
+	switch (dmic->ch_select) {
+	case DMIC_CH_SELECT_LEFT:
+	case DMIC_CH_SELECT_RIGHT:
+		cif_conf.client_ch = 1;
+		break;
+	case DMIC_CH_SELECT_STEREO:
+		cif_conf.client_ch = 2;
+		break;
+	default:
+		dev_err(dai->dev, "invalid DMIC client channels\n");
+		return -EINVAL;
+	}
+
+	srate = params_rate(params);
+	if (dmic->srate_override)
+		srate = dmic->srate_override;
+
+	/*
+	 * DMIC clock rate is a multiple of 'Over Sampling Ratio' and
+	 * 'Sample Rate'. The supported OSR values are 64, 128 and 256.
+	 */
+	clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate;
+
+	ret = clk_set_rate(dmic->clk_dmic, clk_rate);
+	if (ret) {
+		dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n",
+			clk_rate, ret);
+		return ret;
+	}
+
+	regmap_update_bits(dmic->regmap,
+			   /* Reg */
+			   TEGRA210_DMIC_CTRL,
+			   /* Mask */
+			   TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK |
+			   TEGRA210_DMIC_CTRL_OSR_MASK |
+			   TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK,
+			   /* Value */
+			   (dmic->lrsel << LRSEL_POL_SHIFT) |
+			   (dmic->osr_val << OSR_SHIFT) |
+			   ((dmic->ch_select + 1) << CH_SEL_SHIFT));
+
+	/*
+	 * Use LP filter gain register to apply boost.
+	 * Boost Gain control has 100x factor.
+	 */
+	if (dmic->boost_gain)
+		gain_q23 = (gain_q23 * dmic->boost_gain) / 100;
+
+	regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN,
+		     (unsigned int) gain_q23);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
+		break;
+	default:
+		dev_err(dai->dev, "unsupported format!\n");
+		return -ENOTSUPP;
+	}
+
+	if (dmic->audio_bits_override)
+		cif_conf.audio_bits =
+			tegra210_dmic_fmts[dmic->audio_bits_override];
+
+	cif_conf.client_bits = TEGRA_ACIF_BITS_24;
+	cif_conf.mono_conv = dmic->mono_to_stereo;
+	cif_conf.stereo_conv = dmic->stereo_to_mono;
+
+	tegra_set_cif(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL,
+		      &cif_conf);
+
+	return 0;
+}
+
+static int tegra210_dmic_get_control(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
+
+	if (strstr(kcontrol->id.name, "Boost Gain"))
+		ucontrol->value.integer.value[0] = dmic->boost_gain;
+	else if (strstr(kcontrol->id.name, "Channel Select"))
+		ucontrol->value.integer.value[0] = dmic->ch_select;
+	else if (strstr(kcontrol->id.name, "Mono To Stereo"))
+		ucontrol->value.integer.value[0] = dmic->mono_to_stereo;
+	else if (strstr(kcontrol->id.name, "Stereo To Mono"))
+		ucontrol->value.integer.value[0] = dmic->stereo_to_mono;
+	else if (strstr(kcontrol->id.name, "Audio Bit Format"))
+		ucontrol->value.integer.value[0] = dmic->audio_bits_override;
+	else if (strstr(kcontrol->id.name, "Sample Rate"))
+		ucontrol->value.integer.value[0] = dmic->srate_override;
+	else if (strstr(kcontrol->id.name, "Audio Channels"))
+		ucontrol->value.integer.value[0] = dmic->audio_ch_override;
+	else if (strstr(kcontrol->id.name, "OSR Value"))
+		ucontrol->value.integer.value[0] = dmic->osr_val;
+	else if (strstr(kcontrol->id.name, "LR Polarity Select"))
+		ucontrol->value.integer.value[0] = dmic->lrsel;
+
+	return 0;
+}
+
+static int tegra210_dmic_put_control(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
+	int value = ucontrol->value.integer.value[0];
+
+	if (strstr(kcontrol->id.name, "Boost Gain"))
+		dmic->boost_gain = value;
+	else if (strstr(kcontrol->id.name, "Channel Select"))
+		dmic->ch_select = ucontrol->value.integer.value[0];
+	else if (strstr(kcontrol->id.name, "Mono To Stereo"))
+		dmic->mono_to_stereo = value;
+	else if (strstr(kcontrol->id.name, "Stereo To Mono"))
+		dmic->stereo_to_mono = value;
+	else if (strstr(kcontrol->id.name, "Audio Bit Format"))
+		dmic->audio_bits_override = value;
+	else if (strstr(kcontrol->id.name, "Sample Rate"))
+		dmic->srate_override = value;
+	else if (strstr(kcontrol->id.name, "Audio Channels"))
+		dmic->audio_ch_override = value;
+	else if (strstr(kcontrol->id.name, "OSR Value"))
+		dmic->osr_val = value;
+	else if (strstr(kcontrol->id.name, "LR Polarity Select"))
+		dmic->lrsel = value;
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tegra210_dmic_dai_ops = {
+	.hw_params	= tegra210_dmic_hw_params,
+};
+
+/*
+ * Three DAIs are exposed
+ * 1. "CIF" DAI for connecting with XBAR
+ * 2. "DAP" DAI for connecting with CODEC
+ * 3. "DUMMY_SOURCE" can be used when no external
+ *    codec connection is available. In such case
+ *    "DAP" is connected with "DUMMY_SOURCE"
+ */
+static struct snd_soc_dai_driver tegra210_dmic_dais[] = {
+	{
+		.name = "CIF",
+		.capture = {
+			.stream_name = "DMIC Transmit",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &tegra210_dmic_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "DAP",
+		.playback = {
+			.stream_name = "DMIC Receive",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+	},
+	{
+		.name = "DUMMY_SOURCE",
+		.capture = {
+			.stream_name = "Dummy Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+	}
+};
+
+static const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("DMIC TX", NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0),
+	SND_SOC_DAPM_MIC("Dummy Input", NULL),
+};
+
+static const struct snd_soc_dapm_route tegra210_dmic_routes[] = {
+	{ "DMIC TX",	   NULL, "DMIC Receive" },
+	{ "DMIC Transmit", NULL, "DMIC TX" },
+	{ "Dummy Capture", NULL, "Dummy Input" },
+};
+
+static const char * const tegra210_dmic_ch_select[] = {
+	"Left", "Right", "Stereo",
+};
+
+static const struct soc_enum tegra210_dmic_ch_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select),
+			tegra210_dmic_ch_select);
+
+static const char * const tegra210_dmic_mono_conv_text[] = {
+	"ZERO", "COPY",
+};
+
+static const char * const tegra210_dmic_stereo_conv_text[] = {
+	"CH0", "CH1", "AVG",
+};
+
+static const struct soc_enum tegra210_dmic_mono_conv_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text),
+			tegra210_dmic_mono_conv_text);
+
+static const struct soc_enum tegra210_dmic_stereo_conv_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text),
+			tegra210_dmic_stereo_conv_text);
+
+static const char * const tegra210_dmic_format_text[] = {
+	"None",
+	"16",
+	"32",
+};
+
+static const struct soc_enum tegra210_dmic_format_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_format_text),
+			tegra210_dmic_format_text);
+
+static const char * const tegra210_dmic_osr_text[] = {
+	"OSR_64", "OSR_128", "OSR_256",
+};
+
+static const struct soc_enum tegra210_dmic_osr_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text),
+			tegra210_dmic_osr_text);
+
+static const char * const tegra210_dmic_lrsel_text[] = {
+	"Left", "Right",
+};
+
+static const struct soc_enum tegra210_dmic_lrsel_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text),
+			tegra210_dmic_lrsel_text);
+
+static const struct snd_kcontrol_new tegra210_dmic_controls[] = {
+	SOC_SINGLE_EXT("Boost Gain", 0, 0, MAX_BOOST_GAIN, 0,
+		       tegra210_dmic_get_control, tegra210_dmic_put_control),
+	SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum,
+		     tegra210_dmic_get_control, tegra210_dmic_put_control),
+	SOC_ENUM_EXT("Mono To Stereo",
+		     tegra210_dmic_mono_conv_enum, tegra210_dmic_get_control,
+		     tegra210_dmic_put_control),
+	SOC_ENUM_EXT("Stereo To Mono",
+		     tegra210_dmic_stereo_conv_enum, tegra210_dmic_get_control,
+		     tegra210_dmic_put_control),
+	SOC_ENUM_EXT("Audio Bit Format", tegra210_dmic_format_enum,
+		     tegra210_dmic_get_control, tegra210_dmic_put_control),
+	SOC_SINGLE_EXT("Sample Rate", 0, 0, 48000, 0, tegra210_dmic_get_control,
+		       tegra210_dmic_put_control),
+	SOC_SINGLE_EXT("Audio Channels", 0, 0, 2, 0, tegra210_dmic_get_control,
+		       tegra210_dmic_put_control),
+	SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum,
+		     tegra210_dmic_get_control, tegra210_dmic_put_control),
+	SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum,
+		     tegra210_dmic_get_control, tegra210_dmic_put_control),
+};
+
+static const struct snd_soc_component_driver tegra210_dmic_compnt = {
+	.name			= DRV_NAME,
+	.dapm_widgets		= tegra210_dmic_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tegra210_dmic_widgets),
+	.dapm_routes		= tegra210_dmic_routes,
+	.num_dapm_routes	= ARRAY_SIZE(tegra210_dmic_routes),
+	.controls		= tegra210_dmic_controls,
+	.num_controls		= ARRAY_SIZE(tegra210_dmic_controls),
+};
+
+static bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL:
+	case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG:
+	case TEGRA210_DMIC_CTRL:
+	case TEGRA210_DMIC_DBG_CTRL:
+	case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg)
+{
+	if (tegra210_dmic_wr_reg(dev, reg))
+		return true;
+
+	switch (reg) {
+	case TEGRA210_DMIC_TX_STATUS:
+	case TEGRA210_DMIC_TX_INT_STATUS:
+	case TEGRA210_DMIC_STATUS:
+	case TEGRA210_DMIC_INT_STATUS:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA210_DMIC_TX_STATUS:
+	case TEGRA210_DMIC_TX_INT_STATUS:
+	case TEGRA210_DMIC_TX_INT_SET:
+	case TEGRA210_DMIC_SOFT_RESET:
+	case TEGRA210_DMIC_STATUS:
+	case TEGRA210_DMIC_INT_STATUS:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config tegra210_dmic_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4,
+	.writeable_reg = tegra210_dmic_wr_reg,
+	.readable_reg = tegra210_dmic_rd_reg,
+	.volatile_reg = tegra210_dmic_volatile_reg,
+	.reg_defaults = tegra210_dmic_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_dmic_of_match[] = {
+	{ .compatible = "nvidia,tegra210-dmic" },
+	{},
+};
+
+static int tegra210_dmic_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct tegra210_dmic *dmic;
+	void __iomem *regs;
+	int ret = 0;
+
+	dmic = devm_kcalloc(dev, 1, sizeof(*dmic), GFP_KERNEL);
+	if (!dmic)
+		return -ENOMEM;
+
+	dmic->osr_val = DMIC_OSR_64;
+	dmic->ch_select = DMIC_CH_SELECT_STEREO;
+	dmic->lrsel = DMIC_LRSEL_LEFT;
+	dmic->boost_gain = 0;
+	dmic->stereo_to_mono = 0; /* "CH0" */
+
+	dev_set_drvdata(dev, dmic);
+
+	dmic->clk_dmic = devm_clk_get(dev, "dmic");
+	if (IS_ERR(dmic->clk_dmic)) {
+		dev_err(dev, "can't retrieve DMIC clock\n");
+		return PTR_ERR(dmic->clk_dmic);
+	}
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	dmic->regmap = devm_regmap_init_mmio(dev, regs,
+					     &tegra210_dmic_regmap_config);
+	if (IS_ERR(dmic->regmap)) {
+		dev_err(dev, "regmap init failed\n");
+		return PTR_ERR(dmic->regmap);
+	}
+
+	regcache_cache_only(dmic->regmap, true);
+
+	ret = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt,
+					      tegra210_dmic_dais,
+					      ARRAY_SIZE(tegra210_dmic_dais));
+	if (ret) {
+		dev_err(dev, "failed to register DMIC component\n");
+		return ret;
+	}
+
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+static int tegra210_dmic_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra210_dmic_runtime_suspend(&pdev->dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra210_dmic_pm_ops = {
+	SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend,
+			   tegra210_dmic_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_dmic_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra210_dmic_of_match,
+		.pm = &tegra210_dmic_pm_ops,
+	},
+	.probe = tegra210_dmic_probe,
+	.remove = tegra210_dmic_remove,
+};
+module_platform_driver(tegra210_dmic_driver)
+
+MODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match);
diff --git a/sound/soc/tegra/tegra210_dmic.h b/sound/soc/tegra/tegra210_dmic.h
new file mode 100644
index 0000000..a08d136
--- /dev/null
+++ b/sound/soc/tegra/tegra210_dmic.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_dmic.h - Definitions for Tegra210 DMIC driver
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_DMIC_H__
+#define __TEGRA210_DMIC_H__
+
+/* Register offsets from DMIC BASE */
+#define TEGRA210_DMIC_TX_STATUS				0x0c
+#define TEGRA210_DMIC_TX_INT_STATUS			0x10
+#define TEGRA210_DMIC_TX_INT_MASK			0x14
+#define TEGRA210_DMIC_TX_INT_SET			0x18
+#define TEGRA210_DMIC_TX_INT_CLEAR			0x1c
+#define TEGRA210_DMIC_TX_CIF_CTRL			0x20
+#define TEGRA210_DMIC_ENABLE				0x40
+#define TEGRA210_DMIC_SOFT_RESET			0x44
+#define TEGRA210_DMIC_CG				0x48
+#define TEGRA210_DMIC_STATUS				0x4c
+#define TEGRA210_DMIC_INT_STATUS			0x50
+#define TEGRA210_DMIC_CTRL				0x64
+#define TEGRA210_DMIC_DBG_CTRL				0x70
+#define TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4		0x88
+#define TEGRA210_DMIC_LP_FILTER_GAIN			0x8c
+#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_0		0x90
+#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_1		0x94
+#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_2		0x98
+#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_3		0x9c
+#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_4		0xa0
+#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_0		0xa4
+#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_1		0xa8
+#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_2		0xac
+#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_3		0xb0
+#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_4		0xb4
+
+/* Fields in TEGRA210_DMIC_CTRL */
+#define CH_SEL_SHIFT					8
+#define TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK		(0x3 << CH_SEL_SHIFT)
+#define LRSEL_POL_SHIFT					4
+#define TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK		(0x1 << LRSEL_POL_SHIFT)
+#define OSR_SHIFT					0
+#define TEGRA210_DMIC_CTRL_OSR_MASK			(0x3 << OSR_SHIFT)
+
+#define DMIC_OSR_FACTOR					64
+
+#define DEFAULT_GAIN_Q23				0x800000
+
+/* Max boost gain factor used for mixer control */
+#define MAX_BOOST_GAIN 25599
+
+enum tegra_dmic_ch_select {
+	DMIC_CH_SELECT_LEFT,
+	DMIC_CH_SELECT_RIGHT,
+	DMIC_CH_SELECT_STEREO,
+};
+
+enum tegra_dmic_osr {
+	DMIC_OSR_64,
+	DMIC_OSR_128,
+	DMIC_OSR_256,
+};
+
+enum tegra_dmic_lrsel {
+	DMIC_LRSEL_LEFT,
+	DMIC_LRSEL_RIGHT,
+};
+
+struct tegra210_dmic {
+	struct clk *clk_dmic;
+	struct regmap *regmap;
+	unsigned int audio_ch_override;
+	unsigned int audio_bits_override;
+	unsigned int srate_override;
+	unsigned int mono_to_stereo;
+	unsigned int stereo_to_mono;
+	unsigned int boost_gain;
+	unsigned int ch_select;
+	unsigned int osr_val;
+	unsigned int lrsel;
+};
+
+#endif
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (2 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 3/9] ASoC: tegra: add Tegra210 based DMIC driver Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-21  5:15   ` [alsa-devel] " Dmitry Osipenko
  2020-01-20 14:23 ` [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver Sameer Pujar
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, 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@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"
+
+#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 */
+	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)
+{
+	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;
+
+	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 = {
+	.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;
+
+	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);
+
+	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);
+
+	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,
+		.owner = THIS_MODULE,
+		.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@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ASoC I2S driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match);
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;
+	bool loopback;
+};
+
+#endif
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (3 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-24  1:18   ` [alsa-devel] " Dmitry Osipenko
  2020-01-20 14:23 ` [PATCH 6/9] ASoC: tegra: add Tegra186 based DSPK driver Sameer Pujar
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

The Audio Hub (AHUB) comprises a collection of hardware accelerators for
audio pre/post-processing and a programmable full crossbar (XBAR) for
routing audio data across these accelerators in time and in parallel.
AHUB supports multiple interfaces to I2S, DSPK, DMIC etc., XBAR is a
switch used to configure or modify audio routing between HW accelerators
present inside AHUB.

This patch registers AHUB component with ASoC framework. The component
driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
driver exposes AHUB interfaces, which can be used to connect different
components in the ASoC layer. Currently the driver takes care of XBAR
programming to allow audio data flow through various clients of the AHUB.
Makefile and Kconfig support is added to allow to build the driver. The
AHUB component can be enabled in the DT via "nvidia,tegra210-ahub"
compatible binding.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/Kconfig         |  10 +
 sound/soc/tegra/Makefile        |   2 +
 sound/soc/tegra/tegra210_ahub.c | 667 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra210_ahub.h | 125 ++++++++
 4 files changed, 804 insertions(+)
 create mode 100644 sound/soc/tegra/tegra210_ahub.c
 create mode 100644 sound/soc/tegra/tegra210_ahub.h

diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 157fa7a..0c07f63 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -62,6 +62,16 @@ config SND_SOC_TEGRA30_I2S
 	  Tegra30 I2S interface. You will also need to select the individual
 	  machine drivers to support below.
 
+config SND_SOC_TEGRA210_AHUB
+        tristate "Tegra210 AHUB module"
+        depends on SND_SOC_TEGRA
+        help
+	  Config to enable Audio Hub (AHUB) module, which comprises of a
+	  switch called Audio Crossbar (AXBAR) used to configure or modify
+	  the audio routing path between various HW accelerators present in
+	  AHUB.
+          Say Y or M if you want to add support for Tegra210 AHUB module.
+
 config SND_SOC_TEGRA210_DMIC
         tristate "Tegra210 DMIC module"
         depends on SND_SOC_TEGRA
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 1c4457d..5283d47 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -9,6 +9,7 @@ snd-soc-tegra20-spdif-objs := tegra20_spdif.o
 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-ahub-objs := tegra210_ahub.o
 snd-soc-tegra210-dmic-objs := tegra210_dmic.o
 snd-soc-tegra210-i2s-objs := tegra210_i2s.o
 
@@ -22,6 +23,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_AHUB) += snd-soc-tegra210-ahub.o
 obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
 
 # Tegra machine Support
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
new file mode 100644
index 0000000..187edf2
--- /dev/null
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tegra210_ahub.c - Tegra210 AHUB driver
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include "tegra210_ahub.h"
+
+#define DRV_NAME "tegra210-ahub"
+
+static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
+	struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
+	struct soc_enum *e = (struct soc_enum *)kctl->private_value;
+	unsigned int reg, i, bit_pos = 0;
+
+	/*
+	 * Find the bit position of current MUX input.
+	 * If nothing is set, position would be 0 and it corresponds to 'None'.
+	 */
+	for (i = 0; i < ahub->soc_data->reg_count; i++) {
+		unsigned int reg_val;
+
+		reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
+		snd_soc_component_read(cmpnt, reg, &reg_val);
+		reg_val &= ahub->soc_data->mask[i];
+
+		if (reg_val) {
+			bit_pos = ffs(reg_val) +
+				  (8 * cmpnt->val_bytes * i);
+			break;
+		}
+	}
+
+	/* Find index related to the item in array *_ahub_mux_texts[] */
+	for (i = 0; i < e->items; i++) {
+		if (bit_pos == e->values[i]) {
+			uctl->value.enumerated.item[0] = i;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
+				     struct snd_ctl_elem_value *uctl)
+{
+	struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
+	struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctl);
+	struct soc_enum *e = (struct soc_enum *)kctl->private_value;
+	struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { };
+	unsigned int *item = uctl->value.enumerated.item;
+	unsigned int value = e->values[item[0]];
+	unsigned int i, bit_pos, reg_idx = 0, reg_val = 0;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+
+	if (value) {
+		/* Get the register index and value to set */
+		reg_idx = (value - 1) / (8 * cmpnt->val_bytes);
+		bit_pos = (value - 1) % (8 * cmpnt->val_bytes);
+		reg_val = BIT(bit_pos);
+	}
+
+	/*
+	 * Run through all parts of a MUX register to find the state changes.
+	 * There will be an additional update if new MUX input value is from
+	 * different part of the MUX register.
+	 */
+	for (i = 0; i < ahub->soc_data->reg_count; i++) {
+		update[i].reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
+		update[i].val = (i == reg_idx) ? reg_val : 0;
+		update[i].mask = ahub->soc_data->mask[i];
+		update[i].kcontrol = kctl;
+
+		/* Update widget power if state has changed */
+		if (snd_soc_component_test_bits(cmpnt, update[i].reg,
+						update[i].mask, update[i].val))
+			snd_soc_dapm_mux_update_power(dapm, kctl, item[0], e,
+						      &update[i]);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_dai_driver tegra210_ahub_dais[] = {
+	DAI(ADMAIF1),
+	DAI(ADMAIF2),
+	DAI(ADMAIF3),
+	DAI(ADMAIF4),
+	DAI(ADMAIF5),
+	DAI(ADMAIF6),
+	DAI(ADMAIF7),
+	DAI(ADMAIF8),
+	DAI(ADMAIF9),
+	DAI(ADMAIF10),
+	DAI(I2S1),
+	DAI(I2S2),
+	DAI(I2S3),
+	DAI(I2S4),
+	DAI(I2S5),
+	DAI(DMIC1),
+	DAI(DMIC2),
+	DAI(DMIC3),
+};
+
+static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
+	DAI(ADMAIF1),
+	DAI(ADMAIF2),
+	DAI(ADMAIF3),
+	DAI(ADMAIF4),
+	DAI(ADMAIF5),
+	DAI(ADMAIF6),
+	DAI(ADMAIF7),
+	DAI(ADMAIF8),
+	DAI(ADMAIF9),
+	DAI(ADMAIF10),
+	DAI(ADMAIF11),
+	DAI(ADMAIF12),
+	DAI(ADMAIF13),
+	DAI(ADMAIF14),
+	DAI(ADMAIF15),
+	DAI(ADMAIF16),
+	DAI(ADMAIF17),
+	DAI(ADMAIF18),
+	DAI(ADMAIF19),
+	DAI(ADMAIF20),
+	DAI(I2S1),
+	DAI(I2S2),
+	DAI(I2S3),
+	DAI(I2S4),
+	DAI(I2S5),
+	DAI(I2S6),
+	DAI(DMIC1),
+	DAI(DMIC2),
+	DAI(DMIC3),
+	DAI(DMIC4),
+	DAI(DSPK1),
+	DAI(DSPK2),
+};
+
+static const char * const tegra210_ahub_mux_texts[] = {
+	"None",
+	"ADMAIF1",
+	"ADMAIF2",
+	"ADMAIF3",
+	"ADMAIF4",
+	"ADMAIF5",
+	"ADMAIF6",
+	"ADMAIF7",
+	"ADMAIF8",
+	"ADMAIF9",
+	"ADMAIF10",
+	"I2S1",
+	"I2S2",
+	"I2S3",
+	"I2S4",
+	"I2S5",
+	"DMIC1",
+	"DMIC2",
+	"DMIC3",
+};
+
+static const char * const tegra186_ahub_mux_texts[] = {
+	"None",
+	"ADMAIF1",
+	"ADMAIF2",
+	"ADMAIF3",
+	"ADMAIF4",
+	"ADMAIF5",
+	"ADMAIF6",
+	"ADMAIF7",
+	"ADMAIF8",
+	"ADMAIF9",
+	"ADMAIF10",
+	"ADMAIF11",
+	"ADMAIF12",
+	"ADMAIF13",
+	"ADMAIF14",
+	"ADMAIF15",
+	"ADMAIF16",
+	"I2S1",
+	"I2S2",
+	"I2S3",
+	"I2S4",
+	"I2S5",
+	"I2S6",
+	"ADMAIF17",
+	"ADMAIF18",
+	"ADMAIF19",
+	"ADMAIF20",
+	"DMIC1",
+	"DMIC2",
+	"DMIC3",
+	"DMIC4",
+};
+
+static const int tegra210_ahub_mux_values[] = {
+	0,
+	MUX_VALUE(0, 0),
+	MUX_VALUE(0, 1),
+	MUX_VALUE(0, 2),
+	MUX_VALUE(0, 3),
+	MUX_VALUE(0, 4),
+	MUX_VALUE(0, 5),
+	MUX_VALUE(0, 6),
+	MUX_VALUE(0, 7),
+	MUX_VALUE(0, 8),
+	MUX_VALUE(0, 9),
+	MUX_VALUE(0, 16),
+	MUX_VALUE(0, 17),
+	MUX_VALUE(0, 18),
+	MUX_VALUE(0, 19),
+	MUX_VALUE(0, 20),
+};
+
+static const int tegra186_ahub_mux_values[] = {
+	0,
+	MUX_VALUE(0, 0),
+	MUX_VALUE(0, 1),
+	MUX_VALUE(0, 2),
+	MUX_VALUE(0, 3),
+	MUX_VALUE(0, 4),
+	MUX_VALUE(0, 5),
+	MUX_VALUE(0, 6),
+	MUX_VALUE(0, 7),
+	MUX_VALUE(0, 8),
+	MUX_VALUE(0, 9),
+	MUX_VALUE(0, 10),
+	MUX_VALUE(0, 11),
+	MUX_VALUE(0, 12),
+	MUX_VALUE(0, 13),
+	MUX_VALUE(0, 14),
+	MUX_VALUE(0, 15),
+	MUX_VALUE(0, 16),
+	MUX_VALUE(0, 17),
+	MUX_VALUE(0, 18),
+	MUX_VALUE(0, 19),
+	MUX_VALUE(0, 20),
+	MUX_VALUE(0, 21),
+	MUX_VALUE(3, 16),
+	MUX_VALUE(3, 17),
+	MUX_VALUE(3, 18),
+	MUX_VALUE(3, 19),
+	MUX_VALUE(2, 18),
+	MUX_VALUE(2, 19),
+	MUX_VALUE(2, 20),
+	MUX_VALUE(2, 21),
+};
+
+/* Controls for t210 */
+MUX_ENUM_CTRL_DECL(t210_admaif1_tx, 0x00);
+MUX_ENUM_CTRL_DECL(t210_admaif2_tx, 0x01);
+MUX_ENUM_CTRL_DECL(t210_admaif3_tx, 0x02);
+MUX_ENUM_CTRL_DECL(t210_admaif4_tx, 0x03);
+MUX_ENUM_CTRL_DECL(t210_admaif5_tx, 0x04);
+MUX_ENUM_CTRL_DECL(t210_admaif6_tx, 0x05);
+MUX_ENUM_CTRL_DECL(t210_admaif7_tx, 0x06);
+MUX_ENUM_CTRL_DECL(t210_admaif8_tx, 0x07);
+MUX_ENUM_CTRL_DECL(t210_admaif9_tx, 0x08);
+MUX_ENUM_CTRL_DECL(t210_admaif10_tx, 0x09);
+MUX_ENUM_CTRL_DECL(t210_i2s1_tx, 0x10);
+MUX_ENUM_CTRL_DECL(t210_i2s2_tx, 0x11);
+MUX_ENUM_CTRL_DECL(t210_i2s3_tx, 0x12);
+MUX_ENUM_CTRL_DECL(t210_i2s4_tx, 0x13);
+MUX_ENUM_CTRL_DECL(t210_i2s5_tx, 0x14);
+
+/* Controls for t186 */
+MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00);
+MUX_ENUM_CTRL_DECL_186(t186_admaif2_tx, 0x01);
+MUX_ENUM_CTRL_DECL_186(t186_admaif3_tx, 0x02);
+MUX_ENUM_CTRL_DECL_186(t186_admaif4_tx, 0x03);
+MUX_ENUM_CTRL_DECL_186(t186_admaif5_tx, 0x04);
+MUX_ENUM_CTRL_DECL_186(t186_admaif6_tx, 0x05);
+MUX_ENUM_CTRL_DECL_186(t186_admaif7_tx, 0x06);
+MUX_ENUM_CTRL_DECL_186(t186_admaif8_tx, 0x07);
+MUX_ENUM_CTRL_DECL_186(t186_admaif9_tx, 0x08);
+MUX_ENUM_CTRL_DECL_186(t186_admaif10_tx, 0x09);
+MUX_ENUM_CTRL_DECL_186(t186_i2s1_tx, 0x10);
+MUX_ENUM_CTRL_DECL_186(t186_i2s2_tx, 0x11);
+MUX_ENUM_CTRL_DECL_186(t186_i2s3_tx, 0x12);
+MUX_ENUM_CTRL_DECL_186(t186_i2s4_tx, 0x13);
+MUX_ENUM_CTRL_DECL_186(t186_i2s5_tx, 0x14);
+MUX_ENUM_CTRL_DECL_186(t186_admaif11_tx, 0x0a);
+MUX_ENUM_CTRL_DECL_186(t186_admaif12_tx, 0x0b);
+MUX_ENUM_CTRL_DECL_186(t186_admaif13_tx, 0x0c);
+MUX_ENUM_CTRL_DECL_186(t186_admaif14_tx, 0x0d);
+MUX_ENUM_CTRL_DECL_186(t186_admaif15_tx, 0x0e);
+MUX_ENUM_CTRL_DECL_186(t186_admaif16_tx, 0x0f);
+MUX_ENUM_CTRL_DECL_186(t186_i2s6_tx, 0x15);
+MUX_ENUM_CTRL_DECL_186(t186_dspk1_tx, 0x30);
+MUX_ENUM_CTRL_DECL_186(t186_dspk2_tx, 0x31);
+MUX_ENUM_CTRL_DECL_186(t186_admaif17_tx, 0x68);
+MUX_ENUM_CTRL_DECL_186(t186_admaif18_tx, 0x69);
+MUX_ENUM_CTRL_DECL_186(t186_admaif19_tx, 0x6a);
+MUX_ENUM_CTRL_DECL_186(t186_admaif20_tx, 0x6b);
+
+/*
+ * The number of entries in, and order of, this array is closely tied to the
+ * calculation of tegra210_ahub_codec.num_dapm_widgets near the end of
+ * tegra210_ahub_probe()
+ */
+static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = {
+	WIDGETS("ADMAIF1", t210_admaif1_tx),
+	WIDGETS("ADMAIF2", t210_admaif2_tx),
+	WIDGETS("ADMAIF3", t210_admaif3_tx),
+	WIDGETS("ADMAIF4", t210_admaif4_tx),
+	WIDGETS("ADMAIF5", t210_admaif5_tx),
+	WIDGETS("ADMAIF6", t210_admaif6_tx),
+	WIDGETS("ADMAIF7", t210_admaif7_tx),
+	WIDGETS("ADMAIF8", t210_admaif8_tx),
+	WIDGETS("ADMAIF9", t210_admaif9_tx),
+	WIDGETS("ADMAIF10", t210_admaif10_tx),
+	WIDGETS("I2S1", t210_i2s1_tx),
+	WIDGETS("I2S2", t210_i2s2_tx),
+	WIDGETS("I2S3", t210_i2s3_tx),
+	WIDGETS("I2S4", t210_i2s4_tx),
+	WIDGETS("I2S5", t210_i2s5_tx),
+	TX_WIDGETS("DMIC1"),
+	TX_WIDGETS("DMIC2"),
+	TX_WIDGETS("DMIC3"),
+};
+
+static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
+	WIDGETS("ADMAIF1", t186_admaif1_tx),
+	WIDGETS("ADMAIF2", t186_admaif2_tx),
+	WIDGETS("ADMAIF3", t186_admaif3_tx),
+	WIDGETS("ADMAIF4", t186_admaif4_tx),
+	WIDGETS("ADMAIF5", t186_admaif5_tx),
+	WIDGETS("ADMAIF6", t186_admaif6_tx),
+	WIDGETS("ADMAIF7", t186_admaif7_tx),
+	WIDGETS("ADMAIF8", t186_admaif8_tx),
+	WIDGETS("ADMAIF9", t186_admaif9_tx),
+	WIDGETS("ADMAIF10", t186_admaif10_tx),
+	WIDGETS("ADMAIF11", t186_admaif11_tx),
+	WIDGETS("ADMAIF12", t186_admaif12_tx),
+	WIDGETS("ADMAIF13", t186_admaif13_tx),
+	WIDGETS("ADMAIF14", t186_admaif14_tx),
+	WIDGETS("ADMAIF15", t186_admaif15_tx),
+	WIDGETS("ADMAIF16", t186_admaif16_tx),
+	WIDGETS("ADMAIF17", t186_admaif17_tx),
+	WIDGETS("ADMAIF18", t186_admaif18_tx),
+	WIDGETS("ADMAIF19", t186_admaif19_tx),
+	WIDGETS("ADMAIF20", t186_admaif20_tx),
+	WIDGETS("I2S1", t186_i2s1_tx),
+	WIDGETS("I2S2", t186_i2s2_tx),
+	WIDGETS("I2S3", t186_i2s3_tx),
+	WIDGETS("I2S4", t186_i2s4_tx),
+	WIDGETS("I2S5", t186_i2s5_tx),
+	WIDGETS("I2S6", t186_i2s6_tx),
+	TX_WIDGETS("DMIC1"),
+	TX_WIDGETS("DMIC2"),
+	TX_WIDGETS("DMIC3"),
+	TX_WIDGETS("DMIC4"),
+	WIDGETS("DSPK1", t186_dspk1_tx),
+	WIDGETS("DSPK2", t186_dspk2_tx),
+};
+
+#define TEGRA_COMMON_ROUTES(name)					\
+	{ name " RX",		NULL,		name " Receive" },	\
+	{ name " Transmit",	NULL,		name " TX" },		\
+	{ name " TX",		NULL,		name " Mux" },		\
+	{ name " Mux",		"ADMAIF1",	"ADMAIF1 RX" },		\
+	{ name " Mux",		"ADMAIF2",	"ADMAIF2 RX" },		\
+	{ name " Mux",		"ADMAIF3",	"ADMAIF3 RX" },		\
+	{ name " Mux",		"ADMAIF4",	"ADMAIF4 RX" },		\
+	{ name " Mux",		"ADMAIF5",	"ADMAIF5 RX" },		\
+	{ name " Mux",		"ADMAIF6",	"ADMAIF6 RX" },		\
+	{ name " Mux",		"ADMAIF7",	"ADMAIF7 RX" },		\
+	{ name " Mux",		"ADMAIF8",	"ADMAIF8 RX" },		\
+	{ name " Mux",		"ADMAIF9",	"ADMAIF9 RX" },		\
+	{ name " Mux",		"ADMAIF10",	"ADMAIF10 RX" },	\
+	{ name " Mux",		"I2S1",		"I2S1 RX" },		\
+	{ name " Mux",		"I2S2",		"I2S2 RX" },		\
+	{ name " Mux",		"I2S3",		"I2S3 RX" },		\
+	{ name " Mux",		"I2S4",		"I2S4 RX" },		\
+	{ name " Mux",		"I2S5",		"I2S5 RX" },		\
+	{ name " Mux",		"DMIC1",	"DMIC1 RX" },		\
+	{ name " Mux",		"DMIC2",	"DMIC2 RX" },		\
+	{ name " Mux",		"DMIC3",	"DMIC3 RX" },
+
+#define TEGRA186_ONLY_ROUTES(name)					\
+	{ name " Mux",		"ADMAIF11",	"ADMAIF11 RX" },	\
+	{ name " Mux",		"ADMAIF12",	"ADMAIF12 RX" },	\
+	{ name " Mux",		"ADMAIF13",	"ADMAIF13 RX" },	\
+	{ name " Mux",		"ADMAIF14",	"ADMAIF14 RX" },	\
+	{ name " Mux",		"ADMAIF15",	"ADMAIF15 RX" },	\
+	{ name " Mux",		"ADMAIF16",	"ADMAIF16 RX" },	\
+	{ name " Mux",		"ADMAIF17",	"ADMAIF17 RX" },	\
+	{ name " Mux",		"ADMAIF18",	"ADMAIF18 RX" },	\
+	{ name " Mux",		"ADMAIF19",	"ADMAIF19 RX" },	\
+	{ name " Mux",		"ADMAIF20",	"ADMAIF20 RX" },	\
+	{ name " Mux",		"I2S6",		"I2S6 RX" },		\
+	{ name " Mux",		"DMIC4",	"DMIC4 RX" },
+
+#define TEGRA210_ROUTES(name)						\
+	TEGRA_COMMON_ROUTES(name)
+
+#define TEGRA186_ROUTES(name)						\
+	TEGRA_COMMON_ROUTES(name)					\
+	TEGRA186_ONLY_ROUTES(name)
+
+#define IN_OUT_ROUTES(name)						\
+	{ name " RX",		NULL,	name " Receive" },		\
+	{ name " Transmit",	NULL,	name " TX" },
+
+/*
+ * The number of entries in, and order of, this array is closely tied to the
+ * calculation of tegra210_ahub_codec.num_dapm_routes near the end of
+ * tegra210_ahub_probe()
+ */
+static const struct snd_soc_dapm_route tegra210_ahub_routes[] = {
+	TEGRA210_ROUTES("ADMAIF1")
+	TEGRA210_ROUTES("ADMAIF2")
+	TEGRA210_ROUTES("ADMAIF3")
+	TEGRA210_ROUTES("ADMAIF4")
+	TEGRA210_ROUTES("ADMAIF5")
+	TEGRA210_ROUTES("ADMAIF6")
+	TEGRA210_ROUTES("ADMAIF7")
+	TEGRA210_ROUTES("ADMAIF8")
+	TEGRA210_ROUTES("ADMAIF9")
+	TEGRA210_ROUTES("ADMAIF10")
+	TEGRA210_ROUTES("I2S1")
+	TEGRA210_ROUTES("I2S2")
+	TEGRA210_ROUTES("I2S3")
+	TEGRA210_ROUTES("I2S4")
+	TEGRA210_ROUTES("I2S5")
+	IN_OUT_ROUTES("DMIC1")
+	IN_OUT_ROUTES("DMIC2")
+	IN_OUT_ROUTES("DMIC3")
+};
+
+static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
+	TEGRA186_ROUTES("ADMAIF1")
+	TEGRA186_ROUTES("ADMAIF2")
+	TEGRA186_ROUTES("ADMAIF3")
+	TEGRA186_ROUTES("ADMAIF4")
+	TEGRA186_ROUTES("ADMAIF5")
+	TEGRA186_ROUTES("ADMAIF6")
+	TEGRA186_ROUTES("ADMAIF7")
+	TEGRA186_ROUTES("ADMAIF8")
+	TEGRA186_ROUTES("ADMAIF9")
+	TEGRA186_ROUTES("ADMAIF10")
+	TEGRA186_ROUTES("ADMAIF11")
+	TEGRA186_ROUTES("ADMAIF12")
+	TEGRA186_ROUTES("ADMAIF13")
+	TEGRA186_ROUTES("ADMAIF14")
+	TEGRA186_ROUTES("ADMAIF15")
+	TEGRA186_ROUTES("ADMAIF16")
+	TEGRA186_ROUTES("ADMAIF17")
+	TEGRA186_ROUTES("ADMAIF18")
+	TEGRA186_ROUTES("ADMAIF19")
+	TEGRA186_ROUTES("ADMAIF20")
+	TEGRA186_ROUTES("I2S1")
+	TEGRA186_ROUTES("I2S2")
+	TEGRA186_ROUTES("I2S3")
+	TEGRA186_ROUTES("I2S4")
+	TEGRA186_ROUTES("I2S5")
+	TEGRA186_ROUTES("I2S6")
+	TEGRA186_ROUTES("DSPK1")
+	TEGRA186_ROUTES("DSPK2")
+	IN_OUT_ROUTES("DMIC1")
+	IN_OUT_ROUTES("DMIC2")
+	IN_OUT_ROUTES("DMIC3")
+	IN_OUT_ROUTES("DMIC4")
+};
+
+static const struct snd_soc_component_driver tegra210_ahub_component = {
+	.dapm_widgets		= tegra210_ahub_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tegra210_ahub_widgets),
+	.dapm_routes		= tegra210_ahub_routes,
+	.num_dapm_routes	= ARRAY_SIZE(tegra210_ahub_routes),
+};
+
+static const struct snd_soc_component_driver tegra186_ahub_component = {
+	.dapm_widgets = tegra186_ahub_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tegra186_ahub_widgets),
+	.dapm_routes = tegra186_ahub_routes,
+	.num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes),
+};
+
+static const struct regmap_config tegra210_ahub_regmap_config = {
+	.reg_bits		= 32,
+	.val_bits		= 32,
+	.reg_stride		= 4,
+	.max_register		= TEGRA210_MAX_REGISTER_ADDR,
+	.cache_type		= REGCACHE_FLAT,
+};
+
+static const struct regmap_config tegra186_ahub_regmap_config = {
+	.reg_bits		= 32,
+	.val_bits		= 32,
+	.reg_stride		= 4,
+	.max_register		= TEGRA186_MAX_REGISTER_ADDR,
+	.cache_type		= REGCACHE_FLAT,
+};
+
+static const struct tegra_ahub_soc_data soc_data_tegra210 = {
+	.cmpnt_drv	= &tegra210_ahub_component,
+	.dai_drv	= tegra210_ahub_dais,
+	.num_dais	= ARRAY_SIZE(tegra210_ahub_dais),
+	.regmap_config	= &tegra210_ahub_regmap_config,
+	.mask[0]	= TEGRA210_XBAR_REG_MASK_0,
+	.mask[1]	= TEGRA210_XBAR_REG_MASK_1,
+	.mask[2]	= TEGRA210_XBAR_REG_MASK_2,
+	.mask[3]	= TEGRA210_XBAR_REG_MASK_3,
+	.reg_count	= TEGRA210_XBAR_UPDATE_MAX_REG,
+};
+
+static const struct tegra_ahub_soc_data soc_data_tegra186 = {
+	.cmpnt_drv	= &tegra186_ahub_component,
+	.dai_drv	= tegra186_ahub_dais,
+	.num_dais	= ARRAY_SIZE(tegra186_ahub_dais),
+	.regmap_config	= &tegra186_ahub_regmap_config,
+	.mask[0]	= TEGRA186_XBAR_REG_MASK_0,
+	.mask[1]	= TEGRA186_XBAR_REG_MASK_1,
+	.mask[2]	= TEGRA186_XBAR_REG_MASK_2,
+	.mask[3]	= TEGRA186_XBAR_REG_MASK_3,
+	.reg_count	= TEGRA186_XBAR_UPDATE_MAX_REG,
+};
+
+static const struct of_device_id tegra_ahub_of_match[] = {
+	{ .compatible = "nvidia,tegra210-ahub", .data = &soc_data_tegra210 },
+	{ .compatible = "nvidia,tegra186-ahub", .data = &soc_data_tegra186 },
+	{},
+};
+
+static int tegra_ahub_runtime_suspend(struct device *dev)
+{
+	struct tegra_ahub *ahub = dev_get_drvdata(dev);
+
+	regcache_cache_only(ahub->regmap, true);
+	regcache_mark_dirty(ahub->regmap);
+
+	clk_disable_unprepare(ahub->clk);
+
+	return 0;
+}
+
+static int tegra_ahub_runtime_resume(struct device *dev)
+{
+	struct tegra_ahub *ahub = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(ahub->clk);
+	if (ret) {
+		dev_err(dev, "failed to enable AHUB clock, err: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(ahub->regmap, false);
+	regcache_sync(ahub->regmap);
+
+	return 0;
+}
+
+static int tegra_ahub_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct tegra_ahub *ahub;
+	struct tegra_ahub_soc_data *soc_data;
+	void __iomem *regs;
+	struct resource *res;
+	int ret;
+
+	match = of_match_device(tegra_ahub_of_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "error: no device match found\n");
+		return -ENODEV;
+	}
+
+	soc_data = (struct tegra_ahub_soc_data *)match->data;
+
+	ahub = devm_kcalloc(&pdev->dev, 1, sizeof(*ahub), GFP_KERNEL);
+	if (!ahub)
+		return -ENOMEM;
+
+	ahub->soc_data = soc_data;
+
+	platform_set_drvdata(pdev, ahub);
+
+	ahub->clk = devm_clk_get(&pdev->dev, "ahub");
+	if (IS_ERR(ahub->clk)) {
+		dev_err(&pdev->dev, "can't retrieve AHUB clock\n");
+		return PTR_ERR(ahub->clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	ahub->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					     soc_data->regmap_config);
+	if (IS_ERR(ahub->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		return PTR_ERR(ahub->regmap);
+	}
+
+	regcache_cache_only(ahub->regmap, true);
+
+	ret = devm_snd_soc_register_component(&pdev->dev, soc_data->cmpnt_drv,
+					 soc_data->dai_drv, soc_data->num_dais);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register component, err: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (ret < 0)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static int tegra_ahub_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra_ahub_runtime_suspend(&pdev->dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra_ahub_pm_ops = {
+	SET_RUNTIME_PM_OPS(tegra_ahub_runtime_suspend,
+			   tegra_ahub_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra_ahub_driver = {
+	.probe = tegra_ahub_probe,
+	.remove = tegra_ahub_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra_ahub_of_match,
+		.pm = &tegra_ahub_pm_ops,
+	},
+};
+module_platform_driver(tegra_ahub_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ASoC AHUB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra_ahub_of_match);
diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h
new file mode 100644
index 0000000..8a77ab7
--- /dev/null
+++ b/sound/soc/tegra/tegra210_ahub.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_ahub.h - TEGRA210 AHUB
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_AHUB__H__
+#define __TEGRA210_AHUB__H__
+
+/* Tegra210 specific */
+#define TEGRA210_XBAR_PART1_RX				0x200
+#define TEGRA210_XBAR_PART2_RX				0x400
+#define TEGRA210_XBAR_RX_STRIDE				0x4
+#define TEGRA210_XBAR_AUDIO_RX_COUNT			90
+#define TEGRA210_XBAR_REG_MASK_0			0xf1f03ff
+#define TEGRA210_XBAR_REG_MASK_1			0x3f30031f
+#define TEGRA210_XBAR_REG_MASK_2			0xff1cf313
+#define TEGRA210_XBAR_REG_MASK_3			0x0
+#define TEGRA210_XBAR_UPDATE_MAX_REG			3
+/* Tegra186 specific */
+#define TEGRA186_XBAR_PART3_RX				0x600
+#define TEGRA186_XBAR_AUDIO_RX_COUNT			115
+#define TEGRA186_XBAR_REG_MASK_0			0xf3fffff
+#define TEGRA186_XBAR_REG_MASK_1			0x3f310f1f
+#define TEGRA186_XBAR_REG_MASK_2			0xff3cf311
+#define TEGRA186_XBAR_REG_MASK_3			0x3f0f00ff
+#define TEGRA186_XBAR_UPDATE_MAX_REG			4
+
+#define TEGRA_XBAR_UPDATE_MAX_REG (TEGRA186_XBAR_UPDATE_MAX_REG)
+
+#define TEGRA186_MAX_REGISTER_ADDR (TEGRA186_XBAR_PART3_RX +		\
+	(TEGRA210_XBAR_RX_STRIDE * (TEGRA186_XBAR_AUDIO_RX_COUNT - 1)))
+
+#define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX +		\
+	(TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1)))
+
+#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id))
+
+#define MUX_VALUE(npart, nbit) (1 + nbit + npart * 32)
+
+#define DAI(sname)							\
+	{								\
+		.name = #sname,						\
+		.playback = {						\
+			.stream_name = #sname " 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 = #sname " 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,		\
+		},							\
+	}
+
+#define SOC_VALUE_ENUM_WIDE(xreg, shift, xmax, xtexts, xvalues)		\
+	{								\
+		.reg = xreg,						\
+		.shift_l = shift,					\
+		.shift_r = shift,					\
+		.items = xmax,						\
+		.texts = xtexts,					\
+		.values = xvalues,					\
+		.mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0		\
+	}
+
+#define SOC_VALUE_ENUM_WIDE_DECL(name, xreg, shift, xtexts, xvalues)	\
+	static struct soc_enum name =					\
+		SOC_VALUE_ENUM_WIDE(xreg, shift, ARRAY_SIZE(xtexts),	\
+				    xtexts, xvalues)
+
+#define MUX_ENUM_CTRL_DECL(ename, id)					\
+	SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0,		\
+				 tegra210_ahub_mux_texts,		\
+				 tegra210_ahub_mux_values);		\
+	static const struct snd_kcontrol_new ename##_control =		\
+		SOC_DAPM_ENUM_EXT("Route", ename##_enum,		\
+				  tegra_ahub_get_value_enum,		\
+				  tegra_ahub_put_value_enum)
+
+#define MUX_ENUM_CTRL_DECL_186(ename, id)				\
+	SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0,		\
+				 tegra186_ahub_mux_texts,		\
+				 tegra186_ahub_mux_values);		\
+	static const struct snd_kcontrol_new ename##_control =		\
+		SOC_DAPM_ENUM_EXT("Route", ename##_enum,		\
+				  tegra_ahub_get_value_enum,		\
+				  tegra_ahub_put_value_enum)
+
+#define WIDGETS(sname, ename)						\
+	SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0),	\
+	SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0),	\
+	SND_SOC_DAPM_MUX(sname " Mux", SND_SOC_NOPM, 0, 0,		\
+			 &ename##_control)
+
+#define TX_WIDGETS(sname)						\
+	SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0),	\
+	SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0)
+
+struct tegra_ahub_soc_data {
+	const struct regmap_config *regmap_config;
+	const struct snd_soc_component_driver *cmpnt_drv;
+	struct snd_soc_dai_driver *dai_drv;
+	unsigned int mask[4];
+	unsigned int reg_count;
+	unsigned int num_dais;
+};
+
+struct tegra_ahub {
+	const struct tegra_ahub_soc_data *soc_data;
+	struct regmap *regmap;
+	struct clk *clk;
+};
+
+#endif
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 6/9] ASoC: tegra: add Tegra186 based DSPK driver
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (4 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-20 14:23 ` [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver Sameer Pujar
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

The Digital Speaker Controller (DSPK) converts the multi-bit Pulse Code
Modulation (PCM) audio input to oversampled 1-bit Pulse Density Modulation
(PDM) output. From the signal flow perpsective, the DSPK can be viewed as
a PDM transmitter that up-samples the input to the desired sampling rate
by interpolation then converts the oversampled PCM input to the desired
1-bit output via Delta Sigma Modulation (DSM).

This patch registers DSPK component with ASoC framework. The component
driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
driver exposes DSPK 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 DSPK devices can be enabled in the DT via
"nvidia,tegra186-dspk" compatible binding. This driver can be used
on Tegra194 chip as well.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/Kconfig         |  13 +
 sound/soc/tegra/Makefile        |   2 +
 sound/soc/tegra/tegra186_dspk.c | 516 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra186_dspk.h |  73 ++++++
 4 files changed, 604 insertions(+)
 create mode 100644 sound/soc/tegra/tegra186_dspk.c
 create mode 100644 sound/soc/tegra/tegra186_dspk.h

diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 0c07f63..fb77df3 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -93,6 +93,19 @@ config SND_SOC_TEGRA210_I2S
           compatible devices.
           Say Y or M if you want to add support for Tegra210 I2S module.
 
+config SND_SOC_TEGRA186_DSPK
+        tristate "Tegra186 DSPK module"
+        depends on SND_SOC_TEGRA
+        help
+          Config to enable the Digital Speaker Controller (DSPK) which
+          converts the multi-bit Pulse Code Modulation (PCM) audio input to
+          oversampled 1-bit Pulse Desnity Modulation (PDM) output. From the
+          signal flow perspective DSPK can be viewed as a PDM transmitter
+          that up-samples the input to the desired sampling rate by
+          interpolation and then converts the oversampled PCM input to
+          the desired 1-bit output via Delta Sigma Modulation (DSM).
+          Say Y or M if you want to add support for Tegra186 DSPK 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 5283d47..7ad8169 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -12,6 +12,7 @@ snd-soc-tegra-cif-objs := tegra_cif.o
 snd-soc-tegra210-ahub-objs := tegra210_ahub.o
 snd-soc-tegra210-dmic-objs := tegra210_dmic.o
 snd-soc-tegra210-i2s-objs := tegra210_i2s.o
+snd-soc-tegra186-dspk-objs := tegra186_dspk.o
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
@@ -25,6 +26,7 @@ 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_AHUB) += snd-soc-tegra210-ahub.o
 obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
 
 # Tegra machine Support
 snd-soc-tegra-rt5640-objs := tegra_rt5640.o
diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c
new file mode 100644
index 0000000..ea13c84
--- /dev/null
+++ b/sound/soc/tegra/tegra186_dspk.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tegra186_dspk.c - Tegra186 DSPK 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 <linux/of.h>
+#include <sound/pcm_params.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include "tegra186_dspk.h"
+#include "tegra_cif.h"
+
+#define DRV_NAME "tegra186-dspk"
+
+static const struct reg_default tegra186_dspk_reg_defaults[] = {
+	{ TEGRA186_DSPK_AXBAR_RX_INT_MASK, 0x00000007},
+	{ TEGRA186_DSPK_AXBAR_RX_CIF_CTRL, 0x00007700},
+	{ TEGRA186_DSPK_CG,		   0x00000001},
+	{ TEGRA186_DSPK_CORE_CTRL,	   0x00000310},
+	{ TEGRA186_DSPK_CODEC_CTRL,	   0x03000000},
+};
+
+static int tegra186_dspk_get_control(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
+
+	if (strstr(kcontrol->id.name, "FIFO Threshold"))
+		ucontrol->value.integer.value[0] = dspk->rx_fifo_th;
+	else if (strstr(kcontrol->id.name, "OSR Value"))
+		ucontrol->value.integer.value[0] = dspk->osr_val;
+	else if (strstr(kcontrol->id.name, "LR Polarity Select"))
+		ucontrol->value.integer.value[0] = dspk->lrsel;
+	else if (strstr(kcontrol->id.name, "Sample Rate"))
+		ucontrol->value.integer.value[0] = dspk->srate_override;
+	else if (strstr(kcontrol->id.name, "Audio Channels"))
+		ucontrol->value.integer.value[0] = dspk->audio_ch_override;
+	else if (strstr(kcontrol->id.name, "Channel Select"))
+		ucontrol->value.integer.value[0] = dspk->ch_sel;
+	else if (strstr(kcontrol->id.name, "Audio Bit Format"))
+		ucontrol->value.integer.value[0] = dspk->audio_fmt_override;
+	else if (strstr(kcontrol->id.name, "Mono To Stereo"))
+		ucontrol->value.integer.value[0] = dspk->mono_to_stereo;
+	else if (strstr(kcontrol->id.name, "Stereo To Mono"))
+		ucontrol->value.integer.value[0] = dspk->stereo_to_mono;
+
+	return 0;
+}
+
+static int tegra186_dspk_put_control(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
+	int val = ucontrol->value.integer.value[0];
+
+	if (strstr(kcontrol->id.name, "FIFO Threshold"))
+		dspk->rx_fifo_th = val;
+	else if (strstr(kcontrol->id.name, "OSR Value"))
+		dspk->osr_val = val;
+	else if (strstr(kcontrol->id.name, "LR Polarity Select"))
+		dspk->lrsel = val;
+	else if (strstr(kcontrol->id.name, "Sample Rate"))
+		dspk->srate_override = val;
+	else if (strstr(kcontrol->id.name, "Audio Channels"))
+		dspk->audio_ch_override = val;
+	else if (strstr(kcontrol->id.name, "Channel Select"))
+		dspk->ch_sel = val;
+	else if (strstr(kcontrol->id.name, "Audio Bit Format"))
+		dspk->audio_fmt_override = val;
+	else if (strstr(kcontrol->id.name, "Mono To Stereo"))
+		dspk->mono_to_stereo = val;
+	else if (strstr(kcontrol->id.name, "Stereo To Mono"))
+		dspk->stereo_to_mono = val;
+
+	return 0;
+}
+
+static int tegra186_dspk_runtime_suspend(struct device *dev)
+{
+	struct tegra186_dspk *dspk = dev_get_drvdata(dev);
+
+	regcache_cache_only(dspk->regmap, true);
+	regcache_mark_dirty(dspk->regmap);
+
+	clk_disable_unprepare(dspk->clk_dspk);
+
+	return 0;
+}
+
+static int tegra186_dspk_runtime_resume(struct device *dev)
+{
+	struct tegra186_dspk *dspk = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(dspk->clk_dspk);
+	if (ret) {
+		dev_err(dev, "failed to enable DSPK clock, err: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(dspk->regmap, false);
+	regcache_sync(dspk->regmap);
+
+	return 0;
+}
+
+static const int tegra186_dspk_fmts[] = {
+	0,
+	TEGRA_ACIF_BITS_16,
+	TEGRA_ACIF_BITS_32,
+};
+
+static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai);
+	unsigned int channels, srate, dspk_clk;
+	struct device *dev = dai->dev;
+	struct tegra_cif_conf cif_conf;
+	int ret, max_th;
+
+	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+	channels = params_channels(params);
+	cif_conf.audio_ch = channels;
+
+	/* Override audio channel */
+	if (dspk->audio_ch_override)
+		cif_conf.audio_ch = dspk->audio_ch_override;
+
+	/* client channel */
+	switch (dspk->ch_sel) {
+	case DSPK_CH_SELECT_LEFT:
+	case DSPK_CH_SELECT_RIGHT:
+		cif_conf.client_ch = 1;
+		break;
+	case DSPK_CH_SELECT_STEREO:
+		cif_conf.client_ch = 2;
+		break;
+	default:
+		dev_err(dev, "Invalid DSPK client channels\n");
+		return -EINVAL;
+	}
+
+	cif_conf.client_bits = TEGRA_ACIF_BITS_24;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+		cif_conf.client_bits = TEGRA_ACIF_BITS_16;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
+		break;
+	default:
+		dev_err(dev, "unsupported format!\n");
+		return -ENOTSUPP;
+	}
+
+	/* Audio bit format override */
+	if (dspk->audio_fmt_override)
+		cif_conf.audio_bits =
+			tegra186_dspk_fmts[dspk->audio_fmt_override];
+
+	srate = params_rate(params);
+	/* Sample rate override */
+	if (dspk->srate_override)
+		srate = dspk->srate_override;
+
+	/* RX FIFO threshold in terms of frames */
+	max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
+	max_th = (max_th < 0) ? 0 : max_th;
+
+	if (dspk->rx_fifo_th > max_th)
+		dspk->rx_fifo_th = max_th;
+
+	cif_conf.threshold = dspk->rx_fifo_th;
+	cif_conf.mono_conv = dspk->mono_to_stereo;
+	cif_conf.stereo_conv = dspk->stereo_to_mono;
+
+	tegra_set_cif(dspk->regmap, TEGRA186_DSPK_AXBAR_RX_CIF_CTRL,
+		      &cif_conf);
+
+	/*
+	 * DSPK clock and PDM codec clock should be synchronous with 4:1 ratio,
+	 * this is because it takes 4 clock cycles to send out one sample to
+	 * codec by sigma delta modulator. Finally the clock rate is a multiple
+	 * of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'.
+	 */
+	dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO;
+
+	ret = clk_set_rate(dspk->clk_dspk, dspk_clk);
+	if (ret) {
+		dev_err(dev, "can't set DSPK clock rate %u, err: %d\n",
+			dspk_clk, ret);
+
+		return ret;
+	}
+
+	regmap_update_bits(dspk->regmap,
+			   /* Reg */
+			   TEGRA186_DSPK_CORE_CTRL,
+			   /* Mask */
+			   TEGRA186_DSPK_OSR_MASK |
+			   TEGRA186_DSPK_CHANNEL_SELECT_MASK |
+			   TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK,
+			   /* Value */
+			   (dspk->osr_val << DSPK_OSR_SHIFT) |
+			   ((dspk->ch_sel + 1) << CH_SEL_SHIFT) |
+			   (dspk->lrsel << LRSEL_POL_SHIFT));
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tegra186_dspk_dai_ops = {
+	.hw_params	= tegra186_dspk_hw_params,
+};
+
+/*
+ * Three DAIs are exposed
+ * 1. "CIF" DAI for connecting with XBAR
+ * 2. "DAP" DAI for connecting with CODEC
+ * 3. "DUMMY_SINK" can be used when no external
+ *    codec connection is available. In such case
+ *    "DAP" is connected with "DUMMY_SINK"
+ * Order of these DAIs should not be changed, since DAI links in DT refer
+ * to these DAIs depending on the index.
+ */
+static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
+	{
+	    .name = "CIF",
+	    .playback = {
+		.stream_name = "CIF Receive",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	    },
+	},
+	{
+	    .name = "DAP",
+	    .capture = {
+		.stream_name = "DAP Transmit",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	    },
+	    .ops = &tegra186_dspk_dai_ops,
+	    .symmetric_rates = 1,
+	},
+	{
+	    .name = "DUMMY_SINK",
+	    .playback = {
+		.stream_name = "Dummy Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	    },
+	},
+};
+
+static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = {
+	SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
+	SND_SOC_DAPM_SPK("Dummy Output", NULL),
+};
+
+static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {
+	{ "DAP TX",	  NULL, "CIF Receive" },
+	{ "DAP Transmit", NULL, "DAP TX" },
+	{ "Dummy Output", NULL, "Dummy Playback" },
+};
+
+static const char * const tegra186_dspk_format_text[] = {
+	"None",
+	"16",
+	"32",
+};
+
+static const struct soc_enum tegra186_dspk_format_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_format_text),
+			tegra186_dspk_format_text);
+
+static const char * const tegra186_dspk_ch_sel_text[] = {
+	"Left", "Right", "Stereo",
+};
+
+static const struct soc_enum tegra186_dspk_ch_sel_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text),
+			tegra186_dspk_ch_sel_text);
+
+static const char * const tegra186_dspk_osr_text[] = {
+	"OSR_32", "OSR_64", "OSR_128", "OSR_256",
+};
+
+static const struct soc_enum tegra186_dspk_osr_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text),
+			tegra186_dspk_osr_text);
+
+static const char * const tegra186_dspk_lrsel_text[] = {
+	"Left", "Right",
+};
+
+static const char * const tegra186_dspk_mono_conv_text[] = {
+	"ZERO", "COPY",
+};
+
+static const struct soc_enum tegra186_dspk_mono_conv_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+			ARRAY_SIZE(tegra186_dspk_mono_conv_text),
+			tegra186_dspk_mono_conv_text);
+
+static const char * const tegra186_dspk_stereo_conv_text[] = {
+	"CH0", "CH1", "AVG",
+};
+
+static const struct soc_enum tegra186_dspk_stereo_conv_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+			ARRAY_SIZE(tegra186_dspk_stereo_conv_text),
+			tegra186_dspk_stereo_conv_text);
+
+static const struct soc_enum tegra186_dspk_lrsel_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text),
+			tegra186_dspk_lrsel_text);
+
+static const struct snd_kcontrol_new tegrat186_dspk_controls[] = {
+	SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0,
+		       TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0,
+		       tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_SINGLE_EXT("Sample Rate", SND_SOC_NOPM, 0, 48000, 0,
+		       tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_SINGLE_EXT("Audio Channels", SND_SOC_NOPM, 0, 2, 0,
+		       tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("Audio Bit Format", tegra186_dspk_format_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+	SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum,
+		     tegra186_dspk_get_control, tegra186_dspk_put_control),
+};
+
+static const struct snd_soc_component_driver tegra186_dspk_cmpnt = {
+	.dapm_widgets = tegra186_dspk_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets),
+	.dapm_routes = tegra186_dspk_routes,
+	.num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes),
+	.controls = tegrat186_dspk_controls,
+	.num_controls = ARRAY_SIZE(tegrat186_dspk_controls),
+};
+
+/* Regmap callback functions */
+static bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA186_DSPK_AXBAR_RX_INT_MASK ... TEGRA186_DSPK_AXBAR_RX_CIF_CTRL:
+	case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG:
+	case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg)
+{
+	if (tegra186_dspk_wr_reg(dev, reg))
+		return true;
+
+	switch (reg) {
+	case TEGRA186_DSPK_AXBAR_RX_STATUS:
+	case TEGRA186_DSPK_AXBAR_RX_INT_STATUS:
+	case TEGRA186_DSPK_STATUS:
+	case TEGRA186_DSPK_INT_STATUS:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA186_DSPK_AXBAR_RX_STATUS:
+	case TEGRA186_DSPK_AXBAR_RX_INT_STATUS:
+	case TEGRA186_DSPK_STATUS:
+	case TEGRA186_DSPK_INT_STATUS:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config tegra186_dspk_regmap = {
+	.reg_bits		= 32,
+	.reg_stride		= 4,
+	.val_bits		= 32,
+	.max_register		= TEGRA186_DSPK_CODEC_CTRL,
+	.writeable_reg		= tegra186_dspk_wr_reg,
+	.readable_reg		= tegra186_dspk_rd_reg,
+	.volatile_reg		= tegra186_dspk_volatile_reg,
+	.reg_defaults		= tegra186_dspk_reg_defaults,
+	.num_reg_defaults	= ARRAY_SIZE(tegra186_dspk_reg_defaults),
+	.cache_type		= REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra186_dspk_of_match[] = {
+	{ .compatible = "nvidia,tegra186-dspk" },
+	{},
+};
+
+static int tegra186_dspk_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct tegra186_dspk *dspk;
+	void __iomem *regs;
+	int ret = 0;
+
+	dspk = devm_kcalloc(dev, 1, sizeof(*dspk), GFP_KERNEL);
+	if (!dspk)
+		return -ENOMEM;
+
+	dspk->osr_val = DSPK_OSR_64;
+	dspk->lrsel = DSPK_LRSEL_LEFT;
+	dspk->ch_sel = DSPK_CH_SELECT_STEREO;
+	dspk->mono_to_stereo = 0; /* "ZERO" */
+
+	dev_set_drvdata(dev, dspk);
+
+	dspk->clk_dspk = devm_clk_get(dev, "dspk");
+	if (IS_ERR(dspk->clk_dspk)) {
+		dev_err(dev, "can't retrieve DSPK clock\n");
+		return PTR_ERR(dspk->clk_dspk);
+	}
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap);
+	if (IS_ERR(dspk->regmap)) {
+		dev_err(dev, "regmap init failed for DSPK\n");
+		return PTR_ERR(dspk->regmap);
+	}
+
+	regcache_cache_only(dspk->regmap, true);
+
+	ret = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt,
+					      tegra186_dspk_dais,
+					      ARRAY_SIZE(tegra186_dspk_dais));
+	if (ret) {
+		dev_err(dev, "could not register DSPK component, err: %d\n",
+			ret);
+		return ret;
+	}
+
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+static int tegra186_dspk_platform_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra186_dspk_runtime_suspend(&pdev->dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra186_dspk_pm_ops = {
+	SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend,
+			   tegra186_dspk_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra186_dspk_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra186_dspk_of_match,
+		.pm = &tegra186_dspk_pm_ops,
+	},
+	.probe = tegra186_dspk_platform_probe,
+	.remove = tegra186_dspk_platform_remove,
+};
+module_platform_driver(tegra186_dspk_driver);
+
+MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
+MODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>");
+MODULE_DESCRIPTION("Tegra186 ASoC DSPK driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra186_dspk_of_match);
diff --git a/sound/soc/tegra/tegra186_dspk.h b/sound/soc/tegra/tegra186_dspk.h
new file mode 100644
index 0000000..4221499
--- /dev/null
+++ b/sound/soc/tegra/tegra186_dspk.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra186_dspk.h - Definitions for Tegra186 DSPK driver
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA186_DSPK_H__
+#define __TEGRA186_DSPK_H__
+
+/* Register offsets from DSPK BASE */
+#define TEGRA186_DSPK_AXBAR_RX_STATUS			0x0c
+#define TEGRA186_DSPK_AXBAR_RX_INT_STATUS		0x10
+#define TEGRA186_DSPK_AXBAR_RX_INT_MASK			0x14
+#define TEGRA186_DSPK_AXBAR_RX_INT_SET			0x18
+#define TEGRA186_DSPK_AXBAR_RX_INT_CLEAR		0x1c
+#define TEGRA186_DSPK_AXBAR_RX_CIF_CTRL			0x20
+#define TEGRA186_DSPK_ENABLE				0x40
+#define TEGRA186_DSPK_SOFT_RESET			0x44
+#define TEGRA186_DSPK_CG				0x48
+#define TEGRA186_DSPK_STATUS				0x4c
+#define TEGRA186_DSPK_INT_STATUS			0x50
+#define TEGRA186_DSPK_CORE_CTRL				0x60
+#define TEGRA186_DSPK_CODEC_CTRL			0x64
+
+/* DSPK CORE CONTROL fields */
+#define CH_SEL_SHIFT					8
+#define TEGRA186_DSPK_CHANNEL_SELECT_MASK		(0x3 << CH_SEL_SHIFT)
+#define DSPK_OSR_SHIFT					4
+#define TEGRA186_DSPK_OSR_MASK				(0x3 << DSPK_OSR_SHIFT)
+#define LRSEL_POL_SHIFT					0
+#define TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK		(0x1 << LRSEL_POL_SHIFT)
+#define TEGRA186_DSPK_RX_FIFO_DEPTH			64
+
+#define DSPK_OSR_FACTOR					32
+
+/* DSPK interface clock ratio */
+#define DSPK_CLK_RATIO					4
+
+enum tegra_dspk_osr {
+	DSPK_OSR_32,
+	DSPK_OSR_64,
+	DSPK_OSR_128,
+	DSPK_OSR_256,
+};
+
+enum tegra_dspk_ch_sel {
+	DSPK_CH_SELECT_LEFT,
+	DSPK_CH_SELECT_RIGHT,
+	DSPK_CH_SELECT_STEREO,
+};
+
+enum tegra_dspk_lrsel {
+	DSPK_LRSEL_LEFT,
+	DSPK_LRSEL_RIGHT,
+};
+
+struct tegra186_dspk {
+	unsigned int rx_fifo_th;
+	unsigned int osr_val;
+	unsigned int lrsel;
+	unsigned int srate_override;
+	unsigned int audio_ch_override;
+	unsigned int ch_sel; /* Used for client channel override */
+	unsigned int audio_fmt_override;
+	unsigned int mono_to_stereo;
+	unsigned int stereo_to_mono;
+	struct clk *clk_dspk;
+	struct regmap *regmap;
+};
+
+#endif
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (5 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 6/9] ASoC: tegra: add Tegra186 based DSPK driver Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-24  1:28   ` [alsa-devel] " Dmitry Osipenko
  2020-01-20 14:23 ` [PATCH 8/9] arm64: tegra: add AHUB components for few Tegra chips Sameer Pujar
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

ADMAIF is the interface between ADMA and AHUB. Each ADMA channel that
sends/receives data to/from AHUB must intreface through an ADMAIF channel.
ADMA channel sending data to AHUB pairs with an ADMAIF Tx channel and
similarly ADMA channel receiving data from AHUB pairs with an ADMAIF Rx
channel. Buffer size is configuranle for each ADMAIF channel, but currently
SW uses default values.

This patch registers ADMAIF driver with ASoC framework. The component
driver exposes DAPM widgets, routes and kcontrols for the device. The DAI
driver exposes ADMAIF 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 ADMAIF device can be enabled in the DT via
"nvidia,tegra210-admaif" compatible binding.

Tegra PCM driver is updated to expose required PCM interfaces and
snd_pcm_ops callbacks.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 sound/soc/tegra/Kconfig           |  12 +
 sound/soc/tegra/Makefile          |   2 +
 sound/soc/tegra/tegra210_admaif.c | 896 ++++++++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra210_admaif.h | 164 +++++++
 sound/soc/tegra/tegra_pcm.c       | 224 +++++++++-
 sound/soc/tegra/tegra_pcm.h       |  23 +-
 6 files changed, 1319 insertions(+), 2 deletions(-)
 create mode 100644 sound/soc/tegra/tegra210_admaif.c
 create mode 100644 sound/soc/tegra/tegra210_admaif.h

diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index fb77df3..c0a5f0b 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -106,6 +106,18 @@ config SND_SOC_TEGRA186_DSPK
           the desired 1-bit output via Delta Sigma Modulation (DSM).
           Say Y or M if you want to add support for Tegra186 DSPK module.
 
+config SND_SOC_TEGRA210_ADMAIF
+        tristate "Tegra210 ADMAIF module"
+        depends on SND_SOC_TEGRA
+        help
+          Config to enable ADMAIF which is the interface between ADMA and
+          Audio Hub (AHUB). Each ADMA channel that sends/receives data to/
+          from AHUB must interface through an ADMAIF channel. ADMA channel
+          sending data to AHUB pairs with an ADMAIF Tx channel, where as
+          ADMA channel receiving data from AHUB pairs with an ADMAIF Rx
+          channel. Buffer size is confiigurable for each ADMAIIF channel.
+          Say Y or M if you want to add support for Tegra210 ADMAIF 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 7ad8169..1033464 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -13,6 +13,7 @@ snd-soc-tegra210-ahub-objs := tegra210_ahub.o
 snd-soc-tegra210-dmic-objs := tegra210_dmic.o
 snd-soc-tegra210-i2s-objs := tegra210_i2s.o
 snd-soc-tegra186-dspk-objs := tegra186_dspk.o
+snd-soc-tegra210-admaif-objs := tegra210_admaif.o
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o
 obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o
 obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o
 obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o
+obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o
 
 # Tegra machine Support
 snd-soc-tegra-rt5640-objs := tegra_rt5640.o
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
new file mode 100644
index 0000000..278700f
--- /dev/null
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -0,0 +1,896 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tegra210_admaif.c - Tegra ADMAIF driver
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "tegra210_admaif.h"
+#include "tegra_pcm.h"
+#include "tegra_cif.h"
+
+#define DRV_NAME "tegra210-admaif"
+
+#define CH_REG(offset, reg, id)						       \
+	((offset) + (reg) + (TEGRA_ADMAIF_CHANNEL_REG_STRIDE * (id)))
+
+#define CH_TX_REG(reg, id) CH_REG(admaif->soc_data->tx_base, reg, id)
+
+#define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id)
+
+#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base)		       \
+	{ CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001},	       \
+	{ CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), 0x00007700},      \
+	{ CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl},	       \
+	{ CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001},	       \
+	{ CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), 0x00007700},      \
+	{ CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl}
+
+#define ADMAIF_REG_DEFAULTS(id, chip)					       \
+	REG_DEFAULTS((id - 1),						       \
+		chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT,	       \
+		chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT,	       \
+		chip ## _ADMAIF_TX_BASE,				       \
+		chip ## _ADMAIF_RX_BASE)
+
+static const struct reg_default tegra186_admaif_reg_defaults[] = {
+	{(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003},
+	ADMAIF_REG_DEFAULTS(1, TEGRA186),
+	ADMAIF_REG_DEFAULTS(2, TEGRA186),
+	ADMAIF_REG_DEFAULTS(3, TEGRA186),
+	ADMAIF_REG_DEFAULTS(4, TEGRA186),
+	ADMAIF_REG_DEFAULTS(5, TEGRA186),
+	ADMAIF_REG_DEFAULTS(6, TEGRA186),
+	ADMAIF_REG_DEFAULTS(7, TEGRA186),
+	ADMAIF_REG_DEFAULTS(8, TEGRA186),
+	ADMAIF_REG_DEFAULTS(9, TEGRA186),
+	ADMAIF_REG_DEFAULTS(10, TEGRA186),
+	ADMAIF_REG_DEFAULTS(11, TEGRA186),
+	ADMAIF_REG_DEFAULTS(12, TEGRA186),
+	ADMAIF_REG_DEFAULTS(13, TEGRA186),
+	ADMAIF_REG_DEFAULTS(14, TEGRA186),
+	ADMAIF_REG_DEFAULTS(15, TEGRA186),
+	ADMAIF_REG_DEFAULTS(16, TEGRA186),
+	ADMAIF_REG_DEFAULTS(17, TEGRA186),
+	ADMAIF_REG_DEFAULTS(18, TEGRA186),
+	ADMAIF_REG_DEFAULTS(19, TEGRA186),
+	ADMAIF_REG_DEFAULTS(20, TEGRA186)
+};
+
+static const struct reg_default tegra210_admaif_reg_defaults[] = {
+	{(TEGRA_ADMAIF_GLOBAL_CG_0+TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003},
+	ADMAIF_REG_DEFAULTS(1, TEGRA210),
+	ADMAIF_REG_DEFAULTS(2, TEGRA210),
+	ADMAIF_REG_DEFAULTS(3, TEGRA210),
+	ADMAIF_REG_DEFAULTS(4, TEGRA210),
+	ADMAIF_REG_DEFAULTS(5, TEGRA210),
+	ADMAIF_REG_DEFAULTS(6, TEGRA210),
+	ADMAIF_REG_DEFAULTS(7, TEGRA210),
+	ADMAIF_REG_DEFAULTS(8, TEGRA210),
+	ADMAIF_REG_DEFAULTS(9, TEGRA210),
+	ADMAIF_REG_DEFAULTS(10, TEGRA210)
+};
+
+static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+	unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
+	unsigned int num_ch = admaif->soc_data->num_ch;
+	unsigned int rx_base = admaif->soc_data->rx_base;
+	unsigned int tx_base = admaif->soc_data->tx_base;
+	unsigned int global_base = admaif->soc_data->global_base;
+	unsigned int reg_max = admaif->soc_data->regmap_conf->max_register;
+	unsigned int rx_max = rx_base + (num_ch * ch_stride);
+	unsigned int tx_max = tx_base + (num_ch * ch_stride);
+
+	if ((reg >= rx_base) && (reg < rx_max)) {
+		reg = (reg - rx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_RX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) ||
+		    (reg == TEGRA_ADMAIF_RX_SOFT_RESET) ||
+		    (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL))
+			return true;
+	} else if ((reg >= tx_base) && (reg < tx_max)) {
+		reg = (reg - tx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_TX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) ||
+		    (reg == TEGRA_ADMAIF_TX_SOFT_RESET) ||
+		    (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL))
+			return true;
+	} else if ((reg >= global_base) && (reg < reg_max)) {
+		if (reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE))
+			return true;
+	}
+
+	return false;
+}
+
+static bool tegra_admaif_rd_reg(struct device *dev, unsigned int reg)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+	unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
+	unsigned int num_ch = admaif->soc_data->num_ch;
+	unsigned int rx_base = admaif->soc_data->rx_base;
+	unsigned int tx_base = admaif->soc_data->tx_base;
+	unsigned int global_base = admaif->soc_data->global_base;
+	unsigned int reg_max = admaif->soc_data->regmap_conf->max_register;
+	unsigned int rx_max = rx_base + (num_ch * ch_stride);
+	unsigned int tx_max = tx_base + (num_ch * ch_stride);
+
+	if ((reg >= rx_base) && (reg < rx_max)) {
+		reg = (reg - rx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_RX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_RX_STATUS) ||
+		    (reg == TEGRA_ADMAIF_RX_INT_STATUS) ||
+		    (reg == TEGRA_ADMAIF_RX_FIFO_CTRL) ||
+		    (reg == TEGRA_ADMAIF_RX_SOFT_RESET) ||
+		    (reg == TEGRA_ADMAIF_CH_ACIF_RX_CTRL))
+			return true;
+	} else if ((reg >= tx_base) && (reg < tx_max)) {
+		reg = (reg - tx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_TX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_TX_STATUS) ||
+		    (reg == TEGRA_ADMAIF_TX_INT_STATUS) ||
+		    (reg == TEGRA_ADMAIF_TX_FIFO_CTRL) ||
+		    (reg == TEGRA_ADMAIF_TX_SOFT_RESET) ||
+		    (reg == TEGRA_ADMAIF_CH_ACIF_TX_CTRL))
+			return true;
+	} else if ((reg >= global_base) && (reg < reg_max)) {
+		if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_ENABLE)) ||
+		    (reg == (global_base + TEGRA_ADMAIF_GLOBAL_CG_0)) ||
+		    (reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) ||
+		    (reg == (global_base +
+				TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) ||
+		    (reg == (global_base +
+				TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS)))
+			return true;
+	}
+
+	return false;
+}
+
+static bool tegra_admaif_volatile_reg(struct device *dev, unsigned int reg)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+	unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
+	unsigned int num_ch = admaif->soc_data->num_ch;
+	unsigned int rx_base = admaif->soc_data->rx_base;
+	unsigned int tx_base = admaif->soc_data->tx_base;
+	unsigned int global_base = admaif->soc_data->global_base;
+	unsigned int reg_max = admaif->soc_data->regmap_conf->max_register;
+	unsigned int rx_max = rx_base + (num_ch * ch_stride);
+	unsigned int tx_max = tx_base + (num_ch * ch_stride);
+
+	if ((reg >= rx_base) && (reg < rx_max)) {
+		reg = (reg - rx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_RX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_RX_STATUS) ||
+		    (reg == TEGRA_ADMAIF_RX_INT_STATUS) ||
+		    (reg == TEGRA_ADMAIF_RX_SOFT_RESET))
+			return true;
+	} else if ((reg >= tx_base) && (reg < tx_max)) {
+		reg = (reg - tx_base) % ch_stride;
+		if ((reg == TEGRA_ADMAIF_TX_ENABLE) ||
+		    (reg == TEGRA_ADMAIF_TX_STATUS) ||
+		    (reg == TEGRA_ADMAIF_TX_INT_STATUS) ||
+		    (reg == TEGRA_ADMAIF_TX_SOFT_RESET))
+			return true;
+	} else if ((reg >= global_base) && (reg < reg_max)) {
+		if ((reg == (global_base + TEGRA_ADMAIF_GLOBAL_STATUS)) ||
+		    (reg == (global_base +
+				TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS)) ||
+		    (reg == (global_base +
+				TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS)))
+			return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config tegra210_admaif_regmap_config = {
+	.reg_bits		= 32,
+	.reg_stride		= 4,
+	.val_bits		= 32,
+	.max_register		= TEGRA210_ADMAIF_LAST_REG,
+	.writeable_reg		= tegra_admaif_wr_reg,
+	.readable_reg		= tegra_admaif_rd_reg,
+	.volatile_reg		= tegra_admaif_volatile_reg,
+	.reg_defaults		= tegra210_admaif_reg_defaults,
+	.num_reg_defaults	= TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1,
+	.cache_type		= REGCACHE_FLAT,
+};
+
+static const struct regmap_config tegra186_admaif_regmap_config = {
+	.reg_bits		= 32,
+	.reg_stride		= 4,
+	.val_bits		= 32,
+	.max_register		= TEGRA186_ADMAIF_LAST_REG,
+	.writeable_reg		= tegra_admaif_wr_reg,
+	.readable_reg		= tegra_admaif_rd_reg,
+	.volatile_reg		= tegra_admaif_volatile_reg,
+	.reg_defaults		= tegra186_admaif_reg_defaults,
+	.num_reg_defaults	= TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1,
+	.cache_type		= REGCACHE_FLAT,
+};
+
+static int tegra_admaif_runtime_suspend(struct device *dev)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+
+	regcache_cache_only(admaif->regmap, true);
+	regcache_mark_dirty(admaif->regmap);
+
+	return 0;
+}
+
+static int tegra_admaif_runtime_resume(struct device *dev)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+
+	regcache_cache_only(admaif->regmap, false);
+	regcache_sync(admaif->regmap);
+
+	return 0;
+}
+
+static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg,
+				      int valid_bit)
+{
+	switch (valid_bit) {
+	case DATA_8BIT:
+		regmap_update_bits(map, reg, PACK8_EN_MASK, PACK8_EN);
+		regmap_update_bits(map, reg, PACK16_EN_MASK, 0);
+		break;
+	case DATA_16BIT:
+		regmap_update_bits(map, reg, PACK16_EN_MASK, PACK16_EN);
+		regmap_update_bits(map, reg, PACK8_EN_MASK, 0);
+		break;
+	case DATA_32BIT:
+		regmap_update_bits(map, reg, PACK16_EN_MASK, 0);
+		regmap_update_bits(map, reg, PACK8_EN_MASK, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct device *dev = dai->dev;
+	struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+	struct tegra_cif_conf cif_conf;
+	unsigned int reg, path;
+	int valid_bit, channels;
+
+	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_8;
+		cif_conf.client_bits = TEGRA_ACIF_BITS_8;
+		valid_bit = DATA_8BIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+		cif_conf.client_bits = TEGRA_ACIF_BITS_16;
+		valid_bit = DATA_16BIT;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
+		cif_conf.client_bits = TEGRA_ACIF_BITS_32;
+		valid_bit  = DATA_32BIT;
+		break;
+	default:
+		dev_err(dev, "unsupported format!\n");
+		return -ENOTSUPP;
+	}
+
+	channels = params_channels(params);
+	cif_conf.client_ch = channels;
+	cif_conf.audio_ch = channels;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		path = ADMAIF_TX_PATH;
+		reg = CH_TX_REG(TEGRA_ADMAIF_CH_ACIF_TX_CTRL, dai->id);
+	} else {
+		path = ADMAIF_RX_PATH;
+		reg = CH_RX_REG(TEGRA_ADMAIF_CH_ACIF_RX_CTRL, dai->id);
+	}
+
+	if (admaif->audio_ch_override[path][dai->id])
+		cif_conf.audio_ch = admaif->audio_ch_override[path][dai->id];
+
+	if (admaif->client_ch_override[path][dai->id])
+		cif_conf.client_ch = admaif->client_ch_override[path][dai->id];
+
+	cif_conf.mono_conv = admaif->mono_to_stereo[path][dai->id];
+	cif_conf.stereo_conv = admaif->stereo_to_mono[path][dai->id];
+
+	tegra_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
+
+	tegra_set_cif(admaif->regmap, reg, &cif_conf);
+
+	return 0;
+}
+
+static int tegra_admaif_start(struct snd_soc_dai *dai, int direction)
+{
+	struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+	unsigned int reg, mask, val;
+
+	switch (direction) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		mask = TX_ENABLE_MASK;
+		val = TX_ENABLE;
+		reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		mask = RX_ENABLE_MASK;
+		val = RX_ENABLE;
+		reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(admaif->regmap, reg, mask, val);
+
+	return 0;
+}
+
+static int tegra_admaif_stop(struct snd_soc_dai *dai, int direction)
+{
+	struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+	unsigned int enable_reg, status_reg, reset_reg, mask, val;
+	char *dir_name;
+	int ret, enable;
+
+	switch (direction) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		mask = TX_ENABLE_MASK;
+		enable = TX_ENABLE;
+		dir_name = "TX";
+		enable_reg = CH_TX_REG(TEGRA_ADMAIF_TX_ENABLE, dai->id);
+		status_reg = CH_TX_REG(TEGRA_ADMAIF_TX_STATUS, dai->id);
+		reset_reg = CH_TX_REG(TEGRA_ADMAIF_TX_SOFT_RESET, dai->id);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		mask = RX_ENABLE_MASK;
+		enable = RX_ENABLE;
+		dir_name = "RX";
+		enable_reg = CH_RX_REG(TEGRA_ADMAIF_RX_ENABLE, dai->id);
+		status_reg = CH_RX_REG(TEGRA_ADMAIF_RX_STATUS, dai->id);
+		reset_reg = CH_RX_REG(TEGRA_ADMAIF_RX_SOFT_RESET, dai->id);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Disable TX/RX channel */
+	regmap_update_bits(admaif->regmap, enable_reg, mask, ~enable);
+
+	/* Wait until ADMAIF TX/RX status is disabled */
+	ret = regmap_read_poll_timeout_atomic(admaif->regmap, status_reg, val,
+					      !(val & enable), 10, 10000);
+	if (ret < 0)
+		dev_warn(dai->dev, "timeout: failed to disable ADMAIF%d_%s\n",
+			dai->id + 1, dir_name);
+
+	/* SW reset */
+	regmap_update_bits(admaif->regmap, reset_reg, SW_RESET_MASK, SW_RESET);
+
+	/* Wait till SW reset is complete */
+	ret = regmap_read_poll_timeout_atomic(admaif->regmap, reset_reg, val,
+					      !(val & SW_RESET_MASK & SW_RESET),
+					      10, 10000);
+	if (ret < 0) {
+		dev_err(dai->dev, "timeout: SW reset failed for ADMAIF%d_%s\n",
+			dai->id + 1, dir_name);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	int ret;
+
+	ret = snd_dmaengine_pcm_trigger(substream, cmd);
+	if (ret < 0)
+		return ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return tegra_admaif_start(dai, substream->stream);
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		return tegra_admaif_stop(dai, substream->stream);
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct snd_soc_dai_ops tegra_admaif_dai_ops = {
+	.hw_params	= tegra_admaif_hw_params,
+	.trigger	= tegra_admaif_trigger,
+};
+
+static int tegra_admaif_get_control(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value;
+	struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt);
+	long *uctl_val = &ucontrol->value.integer.value[0];
+
+	if (strstr(kcontrol->id.name, "Playback Audio Channels"))
+		*uctl_val = admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg];
+	else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
+		*uctl_val = admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg];
+	else if (strstr(kcontrol->id.name, "Playback Client Channels"))
+		*uctl_val = admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg];
+	else if (strstr(kcontrol->id.name, "Capture Client Channels"))
+		*uctl_val = admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg];
+	else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
+		*uctl_val = admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg];
+	else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
+		*uctl_val = admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg];
+	else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
+		*uctl_val = admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg];
+	else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
+		*uctl_val = admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg];
+
+	return 0;
+}
+
+static int tegra_admaif_put_control(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+	struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value;
+	struct tegra_admaif *admaif = snd_soc_component_get_drvdata(cmpnt);
+	int value = ucontrol->value.integer.value[0];
+
+	if (strstr(kcontrol->id.name, "Playback Audio Channels"))
+		admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
+	else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
+		admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
+	else if (strstr(kcontrol->id.name, "Playback Client Channels"))
+		admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
+	else if (strstr(kcontrol->id.name, "Capture Client Channels"))
+		admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
+	else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
+		admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg] = value;
+	else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
+		admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg] = value;
+	else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
+		admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg] = value;
+	else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
+		admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg] = value;
+
+	return 0;
+}
+
+static int tegra_admaif_dai_probe(struct snd_soc_dai *dai)
+{
+	struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+
+	dai->capture_dma_data = &admaif->capture_dma_data[dai->id];
+	dai->playback_dma_data = &admaif->playback_dma_data[dai->id];
+
+	return 0;
+}
+
+#define ADMAIF_DAI(id)						\
+	{							\
+		.name = "ADMAIF" #id,				\
+		.probe = tegra_admaif_dai_probe,		\
+		.playback = {					\
+			.stream_name = "Playback " #id,		\
+			.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 = "Capture " #id,		\
+			.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 = &tegra_admaif_dai_ops,			\
+	}
+
+static struct snd_soc_dai_driver tegra210_admaif_cmpnt_dais[] = {
+	ADMAIF_DAI(1),
+	ADMAIF_DAI(2),
+	ADMAIF_DAI(3),
+	ADMAIF_DAI(4),
+	ADMAIF_DAI(5),
+	ADMAIF_DAI(6),
+	ADMAIF_DAI(7),
+	ADMAIF_DAI(8),
+	ADMAIF_DAI(9),
+	ADMAIF_DAI(10),
+};
+
+static struct snd_soc_dai_driver tegra186_admaif_cmpnt_dais[] = {
+	ADMAIF_DAI(1),
+	ADMAIF_DAI(2),
+	ADMAIF_DAI(3),
+	ADMAIF_DAI(4),
+	ADMAIF_DAI(5),
+	ADMAIF_DAI(6),
+	ADMAIF_DAI(7),
+	ADMAIF_DAI(8),
+	ADMAIF_DAI(9),
+	ADMAIF_DAI(10),
+	ADMAIF_DAI(11),
+	ADMAIF_DAI(12),
+	ADMAIF_DAI(13),
+	ADMAIF_DAI(14),
+	ADMAIF_DAI(15),
+	ADMAIF_DAI(16),
+	ADMAIF_DAI(17),
+	ADMAIF_DAI(18),
+	ADMAIF_DAI(19),
+	ADMAIF_DAI(20),
+};
+
+static const char * const tegra_admaif_stereo_conv_text[] = {
+	"CH0", "CH1", "AVG",
+};
+
+static const char * const tegra_admaif_mono_conv_text[] = {
+	"ZERO", "COPY",
+};
+
+#define TEGRA_ADMAIF_CHANNEL_CTRL(reg)					  \
+	SOC_SINGLE_EXT("ADMAIF" #reg " Playback Audio Channels", reg - 1, \
+		       0, 16, 0, tegra_admaif_get_control,		  \
+		       tegra_admaif_put_control),			  \
+	SOC_SINGLE_EXT("ADMAIF" #reg " Capture Audio Channels", reg - 1,  \
+		       0, 16, 0, tegra_admaif_get_control,		  \
+		       tegra_admaif_put_control),			  \
+	SOC_SINGLE_EXT("ADMAIF" #reg " Playback Client Channels", reg - 1,\
+		       0, 16, 0, tegra_admaif_get_control,		  \
+		       tegra_admaif_put_control),			  \
+	SOC_SINGLE_EXT("ADMAIF" #reg " Capture Client Channels", reg - 1, \
+		       0, 16, 0, tegra_admaif_get_control,		  \
+		       tegra_admaif_put_control)
+
+/*
+ * Below macro is added to avoid looping over all ADMAIFx controls related
+ * to mono/stereo conversions in get()/put() callbacks.
+ */
+#define NV_SOC_ENUM_EXT(xname, xreg, xhandler_get, xhandler_put, xenum_text)   \
+{									       \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				       \
+	.info = snd_soc_info_enum_double,				       \
+	.name = xname,							       \
+	.get = xhandler_get,						       \
+	.put = xhandler_put,						       \
+	.private_value = (unsigned long)&(struct soc_enum)		       \
+		SOC_ENUM_SINGLE(xreg, 0, ARRAY_SIZE(xenum_text), xenum_text)   \
+}
+
+#define TEGRA_ADMAIF_CIF_CTRL(reg)					       \
+	NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Mono To Stereo", reg - 1,\
+			tegra_admaif_get_control, tegra_admaif_put_control,    \
+			tegra_admaif_mono_conv_text),			       \
+	NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback Stereo To Mono", reg - 1,\
+			tegra_admaif_get_control, tegra_admaif_put_control,    \
+			tegra_admaif_stereo_conv_text),			       \
+	NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Mono To Stereo", reg - 1, \
+			tegra_admaif_get_control, tegra_admaif_put_control,    \
+			tegra_admaif_mono_conv_text),			       \
+	NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture Stereo To Mono", reg - 1, \
+			tegra_admaif_get_control, tegra_admaif_put_control,    \
+			tegra_admaif_stereo_conv_text)
+
+static struct snd_kcontrol_new tegra210_admaif_controls[] = {
+	TEGRA_ADMAIF_CHANNEL_CTRL(1),
+	TEGRA_ADMAIF_CHANNEL_CTRL(2),
+	TEGRA_ADMAIF_CHANNEL_CTRL(3),
+	TEGRA_ADMAIF_CHANNEL_CTRL(4),
+	TEGRA_ADMAIF_CHANNEL_CTRL(5),
+	TEGRA_ADMAIF_CHANNEL_CTRL(6),
+	TEGRA_ADMAIF_CHANNEL_CTRL(7),
+	TEGRA_ADMAIF_CHANNEL_CTRL(8),
+	TEGRA_ADMAIF_CHANNEL_CTRL(9),
+	TEGRA_ADMAIF_CHANNEL_CTRL(10),
+	TEGRA_ADMAIF_CIF_CTRL(1),
+	TEGRA_ADMAIF_CIF_CTRL(2),
+	TEGRA_ADMAIF_CIF_CTRL(3),
+	TEGRA_ADMAIF_CIF_CTRL(4),
+	TEGRA_ADMAIF_CIF_CTRL(5),
+	TEGRA_ADMAIF_CIF_CTRL(6),
+	TEGRA_ADMAIF_CIF_CTRL(7),
+	TEGRA_ADMAIF_CIF_CTRL(8),
+	TEGRA_ADMAIF_CIF_CTRL(9),
+	TEGRA_ADMAIF_CIF_CTRL(10),
+};
+
+static struct snd_kcontrol_new tegra186_admaif_controls[] = {
+	TEGRA_ADMAIF_CHANNEL_CTRL(1),
+	TEGRA_ADMAIF_CHANNEL_CTRL(2),
+	TEGRA_ADMAIF_CHANNEL_CTRL(3),
+	TEGRA_ADMAIF_CHANNEL_CTRL(4),
+	TEGRA_ADMAIF_CHANNEL_CTRL(5),
+	TEGRA_ADMAIF_CHANNEL_CTRL(6),
+	TEGRA_ADMAIF_CHANNEL_CTRL(7),
+	TEGRA_ADMAIF_CHANNEL_CTRL(8),
+	TEGRA_ADMAIF_CHANNEL_CTRL(9),
+	TEGRA_ADMAIF_CHANNEL_CTRL(10),
+	TEGRA_ADMAIF_CHANNEL_CTRL(11),
+	TEGRA_ADMAIF_CHANNEL_CTRL(12),
+	TEGRA_ADMAIF_CHANNEL_CTRL(13),
+	TEGRA_ADMAIF_CHANNEL_CTRL(14),
+	TEGRA_ADMAIF_CHANNEL_CTRL(15),
+	TEGRA_ADMAIF_CHANNEL_CTRL(16),
+	TEGRA_ADMAIF_CHANNEL_CTRL(17),
+	TEGRA_ADMAIF_CHANNEL_CTRL(18),
+	TEGRA_ADMAIF_CHANNEL_CTRL(19),
+	TEGRA_ADMAIF_CHANNEL_CTRL(20),
+	TEGRA_ADMAIF_CIF_CTRL(1),
+	TEGRA_ADMAIF_CIF_CTRL(2),
+	TEGRA_ADMAIF_CIF_CTRL(3),
+	TEGRA_ADMAIF_CIF_CTRL(4),
+	TEGRA_ADMAIF_CIF_CTRL(5),
+	TEGRA_ADMAIF_CIF_CTRL(6),
+	TEGRA_ADMAIF_CIF_CTRL(7),
+	TEGRA_ADMAIF_CIF_CTRL(8),
+	TEGRA_ADMAIF_CIF_CTRL(9),
+	TEGRA_ADMAIF_CIF_CTRL(10),
+	TEGRA_ADMAIF_CIF_CTRL(11),
+	TEGRA_ADMAIF_CIF_CTRL(12),
+	TEGRA_ADMAIF_CIF_CTRL(13),
+	TEGRA_ADMAIF_CIF_CTRL(14),
+	TEGRA_ADMAIF_CIF_CTRL(15),
+	TEGRA_ADMAIF_CIF_CTRL(16),
+	TEGRA_ADMAIF_CIF_CTRL(17),
+	TEGRA_ADMAIF_CIF_CTRL(18),
+	TEGRA_ADMAIF_CIF_CTRL(19),
+	TEGRA_ADMAIF_CIF_CTRL(20),
+};
+
+static const struct snd_soc_component_driver tegra210_admaif_cmpnt = {
+	.name			= DRV_NAME,
+	.controls		= tegra210_admaif_controls,
+	.num_controls		= ARRAY_SIZE(tegra210_admaif_controls),
+	.pcm_construct		= tegra_pcm_construct,
+	.pcm_destruct		= tegra_pcm_destruct,
+	.open			= tegra_pcm_open,
+	.close			= tegra_pcm_close,
+	.hw_params		= tegra_pcm_hw_params,
+	.hw_free		= tegra_pcm_hw_free,
+	.mmap			= tegra_pcm_mmap,
+	.pointer		= tegra_pcm_pointer,
+};
+
+static const struct snd_soc_component_driver tegra186_admaif_cmpnt = {
+	.name			= DRV_NAME,
+	.controls		= tegra186_admaif_controls,
+	.num_controls		= ARRAY_SIZE(tegra186_admaif_controls),
+	.pcm_construct		= tegra_pcm_construct,
+	.pcm_destruct		= tegra_pcm_destruct,
+	.open			= tegra_pcm_open,
+	.close			= tegra_pcm_close,
+	.hw_params		= tegra_pcm_hw_params,
+	.hw_free		= tegra_pcm_hw_free,
+	.mmap			= tegra_pcm_mmap,
+	.pointer		= tegra_pcm_pointer,
+};
+
+static const struct tegra_admaif_soc_data soc_data_tegra210 = {
+	.num_ch		= TEGRA210_ADMAIF_CHANNEL_COUNT,
+	.cmpnt		= &tegra210_admaif_cmpnt,
+	.dais		= tegra210_admaif_cmpnt_dais,
+	.regmap_conf	= &tegra210_admaif_regmap_config,
+	.global_base	= TEGRA210_ADMAIF_GLOBAL_BASE,
+	.tx_base	= TEGRA210_ADMAIF_TX_BASE,
+	.rx_base	= TEGRA210_ADMAIF_RX_BASE,
+};
+
+static struct tegra_admaif_soc_data soc_data_tegra186 = {
+	.num_ch		= TEGRA186_ADMAIF_CHANNEL_COUNT,
+	.cmpnt		= &tegra186_admaif_cmpnt,
+	.dais		= tegra186_admaif_cmpnt_dais,
+	.regmap_conf	= &tegra186_admaif_regmap_config,
+	.global_base	= TEGRA186_ADMAIF_GLOBAL_BASE,
+	.tx_base	= TEGRA186_ADMAIF_TX_BASE,
+	.rx_base	= TEGRA186_ADMAIF_RX_BASE,
+};
+
+
+static const struct of_device_id tegra_admaif_of_match[] = {
+	{ .compatible = "nvidia,tegra210-admaif", .data = &soc_data_tegra210 },
+	{ .compatible = "nvidia,tegra186-admaif", .data = &soc_data_tegra186 },
+	{},
+};
+
+static int tegra_admaif_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct tegra_admaif *admaif;
+	void __iomem *regs;
+	struct resource *res;
+	int ret, i;
+
+	match = of_match_device(tegra_admaif_of_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "error: No device match found\n");
+		return -ENODEV;
+	}
+
+	admaif = devm_kcalloc(&pdev->dev, 1, sizeof(*admaif), GFP_KERNEL);
+	if (!admaif)
+		return -ENOMEM;
+
+	admaif->soc_data = (struct tegra_admaif_soc_data *)match->data;
+	dev_set_drvdata(&pdev->dev, admaif);
+
+	admaif->capture_dma_data =
+		devm_kcalloc(&pdev->dev,
+			     admaif->soc_data->num_ch,
+			     sizeof(struct snd_dmaengine_dai_dma_data),
+			     GFP_KERNEL);
+	if (!admaif->capture_dma_data)
+		return -ENOMEM;
+
+	admaif->playback_dma_data =
+		devm_kcalloc(&pdev->dev,
+			     admaif->soc_data->num_ch,
+			     sizeof(struct snd_dmaengine_dai_dma_data),
+			     GFP_KERNEL);
+	if (!admaif->playback_dma_data)
+		return -ENOMEM;
+
+	for (i = 0; i < ADMAIF_PATHS; i++) {
+		admaif->audio_ch_override[i] =
+			devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
+				     sizeof(unsigned int), GFP_KERNEL);
+		if (!admaif->audio_ch_override[i])
+			return -ENOMEM;
+
+		admaif->client_ch_override[i] =
+			devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
+				     sizeof(unsigned int), GFP_KERNEL);
+		if (!admaif->client_ch_override[i])
+			return -ENOMEM;
+
+		admaif->mono_to_stereo[i] =
+			devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
+				     sizeof(unsigned int), GFP_KERNEL);
+		if (!admaif->mono_to_stereo[i])
+			return -ENOMEM;
+
+		admaif->stereo_to_mono[i] =
+			devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
+				     sizeof(unsigned int), GFP_KERNEL);
+		if (!admaif->stereo_to_mono[i])
+			return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	admaif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					       admaif->soc_data->regmap_conf);
+	if (IS_ERR(admaif->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		return PTR_ERR(admaif->regmap);
+	}
+
+	regcache_cache_only(admaif->regmap, true);
+
+	regmap_update_bits(admaif->regmap, admaif->soc_data->global_base +
+			   TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1);
+
+	for (i = 0; i < admaif->soc_data->num_ch; i++) {
+		admaif->playback_dma_data[i].addr = res->start +
+			CH_TX_REG(TEGRA_ADMAIF_TX_FIFO_WRITE, i);
+
+		admaif->capture_dma_data[i].addr = res->start +
+			CH_RX_REG(TEGRA_ADMAIF_RX_FIFO_READ, i);
+
+		admaif->playback_dma_data[i].addr_width = 32;
+
+		if (of_property_read_string_index(pdev->dev.of_node,
+				"dma-names",
+				(i * 2) + 1,
+				&admaif->playback_dma_data[i].chan_name) < 0) {
+			dev_err(&pdev->dev,
+				"missing property nvidia,dma-names\n");
+			return -ENODEV;
+		}
+
+		admaif->capture_dma_data[i].addr_width = 32;
+
+		if (of_property_read_string_index(pdev->dev.of_node,
+				"dma-names",
+				(i * 2),
+				&admaif->capture_dma_data[i].chan_name) < 0) {
+			dev_err(&pdev->dev,
+				"missing property nvidia,dma-names\n");
+			return -ENODEV;
+		}
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      admaif->soc_data->cmpnt,
+					      admaif->soc_data->dais,
+					      admaif->soc_data->num_ch);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "could not register component, err: %d\n",
+			ret);
+		return ret;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static int tegra_admaif_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra_admaif_runtime_suspend(&pdev->dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra_admaif_pm_ops = {
+	SET_RUNTIME_PM_OPS(tegra_admaif_runtime_suspend,
+			   tegra_admaif_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra_admaif_driver = {
+	.probe = tegra_admaif_probe,
+	.remove = tegra_admaif_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra_admaif_of_match,
+		.pm = &tegra_admaif_pm_ops,
+	},
+};
+module_platform_driver(tegra_admaif_driver);
+
+MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ASoC ADMAIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra_admaif_of_match);
diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h
new file mode 100644
index 0000000..1743c0a
--- /dev/null
+++ b/sound/soc/tegra/tegra210_admaif.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_admaif.h - Tegra ADMAIF registers
+ *
+ * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA_ADMAIF_H__
+#define __TEGRA_ADMAIF_H__
+
+#define TEGRA_ADMAIF_CHANNEL_REG_STRIDE			0x40
+/* Tegra210 specific */
+#define TEGRA210_ADMAIF_LAST_REG			0x75f
+#define TEGRA210_ADMAIF_CHANNEL_COUNT			10
+#define TEGRA210_ADMAIF_RX_BASE				0x0
+#define TEGRA210_ADMAIF_TX_BASE				0x300
+#define TEGRA210_ADMAIF_GLOBAL_BASE			0x700
+/* Tegra186 specific */
+#define TEGRA186_ADMAIF_LAST_REG			0xd5f
+#define TEGRA186_ADMAIF_CHANNEL_COUNT			20
+#define TEGRA186_ADMAIF_RX_BASE				0x0
+#define TEGRA186_ADMAIF_TX_BASE				0x500
+#define TEGRA186_ADMAIF_GLOBAL_BASE			0xd00
+/* Global registers */
+#define TEGRA_ADMAIF_GLOBAL_ENABLE			0x0
+#define TEGRA_ADMAIF_GLOBAL_CG_0			0x8
+#define TEGRA_ADMAIF_GLOBAL_STATUS			0x10
+#define TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS		0x20
+#define TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS		0x24
+/* RX channel registers */
+#define TEGRA_ADMAIF_RX_ENABLE				0x0
+#define TEGRA_ADMAIF_RX_SOFT_RESET			0x4
+#define TEGRA_ADMAIF_RX_STATUS				0xc
+#define TEGRA_ADMAIF_RX_INT_STATUS			0x10
+#define TEGRA_ADMAIF_RX_INT_MASK			0x14
+#define TEGRA_ADMAIF_RX_INT_SET				0x18
+#define TEGRA_ADMAIF_RX_INT_CLEAR			0x1c
+#define TEGRA_ADMAIF_CH_ACIF_RX_CTRL			0x20
+#define TEGRA_ADMAIF_RX_FIFO_CTRL			0x28
+#define TEGRA_ADMAIF_RX_FIFO_READ			0x2c
+/* TX channel registers */
+#define TEGRA_ADMAIF_TX_ENABLE				0x0
+#define TEGRA_ADMAIF_TX_SOFT_RESET			0x4
+#define TEGRA_ADMAIF_TX_STATUS				0xc
+#define TEGRA_ADMAIF_TX_INT_STATUS			0x10
+#define TEGRA_ADMAIF_TX_INT_MASK			0x14
+#define TEGRA_ADMAIF_TX_INT_SET				0x18
+#define TEGRA_ADMAIF_TX_INT_CLEAR			0x1c
+#define TEGRA_ADMAIF_CH_ACIF_TX_CTRL			0x20
+#define TEGRA_ADMAIF_TX_FIFO_CTRL			0x28
+#define TEGRA_ADMAIF_TX_FIFO_WRITE			0x2c
+/* Bit fields */
+#define PACK8_EN_SHIFT					31
+#define PACK8_EN_MASK					(1 << PACK8_EN_SHIFT)
+#define PACK8_EN					(1 << PACK8_EN_SHIFT)
+#define PACK16_EN_SHIFT					30
+#define PACK16_EN_MASK					(1 << PACK16_EN_SHIFT)
+#define PACK16_EN					(1 << PACK16_EN_SHIFT)
+#define TX_ENABLE_SHIFT					0
+#define TX_ENABLE_MASK					(1 << TX_ENABLE_SHIFT)
+#define TX_ENABLE					(1 << TX_ENABLE_SHIFT)
+#define RX_ENABLE_SHIFT					0
+#define RX_ENABLE_MASK					(1 << RX_ENABLE_SHIFT)
+#define RX_ENABLE					(1 << RX_ENABLE_SHIFT)
+#define SW_RESET_MASK					1
+#define SW_RESET					1
+/* Default values - Tegra210 */
+#define TEGRA210_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT	0x00000300
+#define TEGRA210_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT	0x00000304
+#define TEGRA210_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT	0x00000208
+#define TEGRA210_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT	0x0000020b
+#define TEGRA210_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT	0x0000020e
+#define TEGRA210_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT	0x00000211
+#define TEGRA210_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT	0x00000214
+#define TEGRA210_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT	0x00000217
+#define TEGRA210_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT	0x0000021a
+#define TEGRA210_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT	0x0000021d
+#define TEGRA210_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT	0x02000300
+#define TEGRA210_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT	0x02000304
+#define TEGRA210_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT	0x01800208
+#define TEGRA210_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT	0x0180020b
+#define TEGRA210_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT	0x0180020e
+#define TEGRA210_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT	0x01800211
+#define TEGRA210_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT	0x01800214
+#define TEGRA210_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT	0x01800217
+#define TEGRA210_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT	0x0180021a
+#define TEGRA210_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT	0x0180021d
+/* Default values - Tegra186 */
+#define TEGRA186_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT	0x00000300
+#define TEGRA186_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT	0x00000304
+#define TEGRA186_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT	0x00000308
+#define TEGRA186_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT	0x0000030c
+#define TEGRA186_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT	0x00000210
+#define TEGRA186_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT	0x00000213
+#define TEGRA186_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT	0x00000216
+#define TEGRA186_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT	0x00000219
+#define TEGRA186_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT	0x0000021c
+#define TEGRA186_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT	0x0000021f
+#define TEGRA186_ADMAIF_RX11_FIFO_CTRL_REG_DEFAULT	0x00000222
+#define TEGRA186_ADMAIF_RX12_FIFO_CTRL_REG_DEFAULT	0x00000225
+#define TEGRA186_ADMAIF_RX13_FIFO_CTRL_REG_DEFAULT	0x00000228
+#define TEGRA186_ADMAIF_RX14_FIFO_CTRL_REG_DEFAULT	0x0000022b
+#define TEGRA186_ADMAIF_RX15_FIFO_CTRL_REG_DEFAULT	0x0000022e
+#define TEGRA186_ADMAIF_RX16_FIFO_CTRL_REG_DEFAULT	0x00000231
+#define TEGRA186_ADMAIF_RX17_FIFO_CTRL_REG_DEFAULT	0x00000234
+#define TEGRA186_ADMAIF_RX18_FIFO_CTRL_REG_DEFAULT	0x00000237
+#define TEGRA186_ADMAIF_RX19_FIFO_CTRL_REG_DEFAULT	0x0000023a
+#define TEGRA186_ADMAIF_RX20_FIFO_CTRL_REG_DEFAULT	0x0000023d
+#define TEGRA186_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT	0x02000300
+#define TEGRA186_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT	0x02000304
+#define TEGRA186_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT	0x02000308
+#define TEGRA186_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT	0x0200030c
+#define TEGRA186_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT	0x01800210
+#define TEGRA186_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT	0x01800213
+#define TEGRA186_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT	0x01800216
+#define TEGRA186_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT	0x01800219
+#define TEGRA186_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT	0x0180021c
+#define TEGRA186_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT	0x0180021f
+#define TEGRA186_ADMAIF_TX11_FIFO_CTRL_REG_DEFAULT	0x01800222
+#define TEGRA186_ADMAIF_TX12_FIFO_CTRL_REG_DEFAULT	0x01800225
+#define TEGRA186_ADMAIF_TX13_FIFO_CTRL_REG_DEFAULT	0x01800228
+#define TEGRA186_ADMAIF_TX14_FIFO_CTRL_REG_DEFAULT	0x0180022b
+#define TEGRA186_ADMAIF_TX15_FIFO_CTRL_REG_DEFAULT	0x0180022e
+#define TEGRA186_ADMAIF_TX16_FIFO_CTRL_REG_DEFAULT	0x01800231
+#define TEGRA186_ADMAIF_TX17_FIFO_CTRL_REG_DEFAULT	0x01800234
+#define TEGRA186_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT	0x01800237
+#define TEGRA186_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT	0x0180023a
+#define TEGRA186_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT	0x0180023d
+
+enum {
+	DATA_8BIT,
+	DATA_16BIT,
+	DATA_32BIT
+};
+
+enum {
+	ADMAIF_RX_PATH,
+	ADMAIF_TX_PATH,
+	ADMAIF_PATHS,
+};
+
+struct tegra_admaif_soc_data {
+	const struct snd_soc_component_driver *cmpnt;
+	const struct regmap_config *regmap_conf;
+	struct snd_soc_dai_driver *dais;
+	unsigned int global_base;
+	unsigned int tx_base;
+	unsigned int rx_base;
+	unsigned int num_ch;
+};
+
+struct tegra_admaif {
+	struct snd_dmaengine_dai_dma_data *capture_dma_data;
+	struct snd_dmaengine_dai_dma_data *playback_dma_data;
+	const struct tegra_admaif_soc_data *soc_data;
+	unsigned int *audio_ch_override[ADMAIF_PATHS];
+	unsigned int *client_ch_override[ADMAIF_PATHS];
+	unsigned int *mono_to_stereo[ADMAIF_PATHS];
+	unsigned int *stereo_to_mono[ADMAIF_PATHS];
+	struct regmap *regmap;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index f246df8..497a54d 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -16,12 +16,12 @@
  */
 
 #include <linux/module.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
-
 #include "tegra_pcm.h"
 
 static const struct snd_pcm_hardware tegra_pcm_hardware = {
@@ -67,6 +67,228 @@ void tegra_pcm_platform_unregister(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
 
+int tegra_pcm_open(struct snd_soc_component *component,
+		   struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_dmaengine_dai_dma_data *dmap;
+	struct dma_chan *chan;
+	int ret;
+
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	/* Set HW params now that initialization is complete */
+	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+
+	/* Ensure period size is multiple of 8 */
+	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
+	if (ret) {
+		dev_err(rtd->dev, "failed to set constraint %d\n", ret);
+		return ret;
+	}
+
+	chan = dma_request_slave_channel(rtd->cpu_dai->dev, dmap->chan_name);
+	if (!chan) {
+		dev_err(rtd->cpu_dai->dev,
+			"dmaengine request slave channel failed! (%s)\n",
+			dmap->chan_name);
+		return -ENODEV;
+	}
+
+	ret = snd_dmaengine_pcm_open(substream, chan);
+	if (ret) {
+		dev_err(rtd->dev,
+			"dmaengine pcm open failed with err %d (%s)\n", ret,
+			dmap->chan_name);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_open);
+
+int tegra_pcm_close(struct snd_soc_component *component,
+		    struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	snd_dmaengine_pcm_close_release_chan(substream);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_close);
+
+int tegra_pcm_hw_params(struct snd_soc_component *component,
+			struct snd_pcm_substream *substream,
+			struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_dmaengine_dai_dma_data *dmap;
+	struct dma_slave_config slave_config;
+	struct dma_chan *chan;
+	int ret;
+
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	if (!dmap)
+		return 0;
+
+	chan = snd_dmaengine_pcm_get_chan(substream);
+
+	ret = snd_hwparams_to_dma_slave_config(substream, params,
+					       &slave_config);
+	if (ret) {
+		dev_err(rtd->dev, "hw params config failed with err %d\n", ret);
+		return ret;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		slave_config.dst_addr = dmap->addr;
+		slave_config.dst_maxburst = 8;
+	} else {
+		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		slave_config.src_addr = dmap->addr;
+		slave_config.src_maxburst = 8;
+	}
+
+	ret = dmaengine_slave_config(chan, &slave_config);
+	if (ret < 0) {
+		dev_err(rtd->dev, "dma slave config failed with err %d\n", ret);
+		return ret;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_hw_params);
+
+int tegra_pcm_hw_free(struct snd_soc_component *component,
+		      struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_hw_free);
+
+int tegra_pcm_mmap(struct snd_soc_component *component,
+		   struct snd_pcm_substream *substream,
+		   struct vm_area_struct *vma)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
+			   runtime->dma_addr, runtime->dma_bytes);
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_mmap);
+
+snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
+				    struct snd_pcm_substream *substream)
+{
+	return snd_dmaengine_pcm_pointer(substream);
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_pointer);
+
+static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
+					    size_t size)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+	buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->private_data = NULL;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->bytes = size;
+
+	return 0;
+}
+
+static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+
+	substream = pcm->streams[stream].substream;
+	if (!substream)
+		return;
+
+	buf = &substream->dma_buffer;
+	if (!buf->area)
+		return;
+
+	dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr);
+	buf->area = NULL;
+}
+
+static int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd,
+	size_t size)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ret = tegra_pcm_preallocate_dma_buffer(pcm,
+						SNDRV_PCM_STREAM_PLAYBACK,
+						size);
+		if (ret)
+			goto err;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = tegra_pcm_preallocate_dma_buffer(pcm,
+						SNDRV_PCM_STREAM_CAPTURE,
+						size);
+		if (ret)
+			goto err_free_play;
+	}
+
+	return 0;
+
+err_free_play:
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+err:
+	return ret;
+}
+
+int tegra_pcm_construct(struct snd_soc_component *component,
+			struct snd_soc_pcm_runtime *rtd)
+{
+	return tegra_pcm_dma_allocate(rtd, tegra_pcm_hardware.buffer_bytes_max);
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_construct);
+
+void tegra_pcm_destruct(struct snd_soc_component *component,
+			struct snd_pcm *pcm)
+{
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+EXPORT_SYMBOL_GPL(tegra_pcm_destruct);
+
 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 MODULE_DESCRIPTION("Tegra PCM ASoC driver");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index 0433372..fe138a4 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -17,8 +17,29 @@
 #ifndef __TEGRA_PCM_H__
 #define __TEGRA_PCM_H__
 
-struct snd_dmaengine_pcm_config;
+#include <sound/dmaengine_pcm.h>
+#include <sound/asound.h>
 
+int tegra_pcm_construct(struct snd_soc_component *component,
+			struct snd_soc_pcm_runtime *rtd);
+void tegra_pcm_destruct(struct snd_soc_component *component,
+			struct snd_pcm *pcm);
+int tegra_pcm_open(struct snd_soc_component *component,
+		   struct snd_pcm_substream *substream);
+int tegra_pcm_close(struct snd_soc_component *component,
+		    struct snd_pcm_substream *substream);
+int tegra_pcm_hw_params(struct snd_soc_component *component,
+			struct snd_pcm_substream *substream,
+			struct snd_pcm_hw_params *params);
+int tegra_pcm_hw_free(struct snd_soc_component *component,
+		      struct snd_pcm_substream *substream);
+int tegra_pcm_mmap(struct snd_soc_component *component,
+		   struct snd_pcm_substream *substream,
+		   struct vm_area_struct *vma);
+int tegra_pcm_trigger(struct snd_soc_component *component,
+		      struct snd_pcm_substream *substream, int cmd);
+snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component,
+				    struct snd_pcm_substream *substream);
 int tegra_pcm_platform_register(struct device *dev);
 int tegra_pcm_platform_register_with_chan_names(struct device *dev,
 				struct snd_dmaengine_pcm_config *config,
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 8/9] arm64: tegra: add AHUB components for few Tegra chips
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (6 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-20 14:23 ` [PATCH 9/9] arm64: tegra: enable AHUB modules " Sameer Pujar
  2020-01-28 10:49 ` [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

This patch adds few AHUB modules for Tegra210, Tegra186 and Tegra194.
Following modules are common to all above SoCs,
 * AHUB added as a child node under ACONNECT
 * AHUB includes many HW accelerators and below components are added
   as its children.
   * ADMAIF
   * I2S
   * DMIC
   * DSPK (added for Tegra186 and Tegra194 only, since Tegra210 does
     not have this module)

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 231 ++++++++++++++++++++++++++++-
 arch/arm64/boot/dts/nvidia/tegra194.dtsi | 239 ++++++++++++++++++++++++++++++-
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 145 +++++++++++++++++++
 3 files changed, 613 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index c905527..04a1b4e 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -82,7 +82,7 @@
 		ranges = <0x02900000 0x0 0x02900000 0x200000>;
 		status = "disabled";
 
-		dma-controller@2930000 {
+		adma: dma-controller@2930000 {
 			compatible = "nvidia,tegra186-adma";
 			reg = <0x02930000 0x20000>;
 			interrupt-parent = <&agic>;
@@ -137,6 +137,235 @@
 			clock-names = "clk";
 			status = "disabled";
 		};
+
+		tegra_ahub: ahub@2900800 {
+			compatible = "nvidia,tegra186-ahub";
+			reg = <0x02900800 0x800>;
+			clocks = <&bpmp TEGRA186_CLK_AHUB>;
+			clock-names = "ahub";
+			assigned-clocks = <&bpmp TEGRA186_CLK_AHUB>;
+			assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x02900800 0x02900800 0x11800>;
+			#sound-dai-cells = <1>;
+			status = "disabled";
+
+			tegra_admaif: admaif@290f000 {
+				compatible = "nvidia,tegra186-admaif";
+				reg = <0x0290f000 0x1000>;
+				dmas = <&adma 1>, <&adma 1>,
+				       <&adma 2>, <&adma 2>,
+				       <&adma 3>, <&adma 3>,
+				       <&adma 4>, <&adma 4>,
+				       <&adma 5>, <&adma 5>,
+				       <&adma 6>, <&adma 6>,
+				       <&adma 7>, <&adma 7>,
+				       <&adma 8>, <&adma 8>,
+				       <&adma 9>, <&adma 9>,
+				       <&adma 10>, <&adma 10>,
+				       <&adma 11>, <&adma 11>,
+				       <&adma 12>, <&adma 12>,
+				       <&adma 13>, <&adma 13>,
+				       <&adma 14>, <&adma 14>,
+				       <&adma 15>, <&adma 15>,
+				       <&adma 16>, <&adma 16>,
+				       <&adma 17>, <&adma 17>,
+				       <&adma 18>, <&adma 18>,
+				       <&adma 19>, <&adma 19>,
+				       <&adma 20>, <&adma 20>;
+				dma-names = "rx1", "tx1",
+					    "rx2", "tx2",
+					    "rx3", "tx3",
+					    "rx4", "tx4",
+					    "rx5", "tx5",
+					    "rx6", "tx6",
+					    "rx7", "tx7",
+					    "rx8", "tx8",
+					    "rx9", "tx9",
+					    "rx10", "tx10",
+					    "rx11", "tx11",
+					    "rx12", "tx12",
+					    "rx13", "tx13",
+					    "rx14", "tx14",
+					    "rx15", "tx15",
+					    "rx16", "tx16",
+					    "rx17", "tx17",
+					    "rx18", "tx18",
+					    "rx19", "tx19",
+					    "rx20", "tx20";
+				#sound-dai-cells = <1>;
+				status = "disabled";
+			};
+
+			tegra_i2s1: i2s@2901000 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901000 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S1>,
+					 <&bpmp TEGRA186_CLK_I2S1_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S1>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S1";
+				status = "disabled";
+			};
+
+			tegra_i2s2: i2s@2901100 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901100 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S2>,
+					 <&bpmp TEGRA186_CLK_I2S2_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S2>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S2";
+				status = "disabled";
+			};
+
+			tegra_i2s3: i2s@2901200 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901200 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S3>,
+					 <&bpmp TEGRA186_CLK_I2S3_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S3>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S3";
+				status = "disabled";
+			};
+
+			tegra_i2s4: i2s@2901300 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901300 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S4>,
+					 <&bpmp TEGRA186_CLK_I2S4_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S4>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S4";
+				status = "disabled";
+			};
+
+			tegra_i2s5: i2s@2901400 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901400 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S5>,
+					 <&bpmp TEGRA186_CLK_I2S5_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S5>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S5";
+				status = "disabled";
+			};
+
+			tegra_i2s6: i2s@2901500 {
+				compatible = "nvidia,tegra186-i2s",
+					     "nvidia,tegra210-i2s";
+				reg = <0x2901500 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_I2S6>,
+					 <&bpmp TEGRA186_CLK_I2S6_SYNC_INPUT>;
+				clock-names = "i2s", "sync_input";
+				assigned-clocks = <&bpmp TEGRA186_CLK_I2S6>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S6";
+				status = "disabled";
+			};
+
+			tegra_dmic1: dmic@2904000 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x2904000 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DMIC1>;
+				clock-names = "dmic";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DMIC1>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC1";
+				status = "disabled";
+			};
+
+			tegra_dmic2: dmic@2904100 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x2904100 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DMIC2>;
+				clock-names = "dmic";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DMIC2>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC2";
+				status = "disabled";
+			};
+
+			tegra_dmic3: dmic@2904200 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x2904200 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DMIC3>;
+				clock-names = "dmic";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DMIC3>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC3";
+				status = "disabled";
+			};
+
+			tegra_dmic4: dmic@2904300 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x2904300 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DMIC4>;
+				clock-names = "dmic";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DMIC4>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC4";
+				status = "disabled";
+			};
+
+			tegra_dspk1: dspk@2905000 {
+				compatible = "nvidia,tegra186-dspk";
+				reg = <0x2905000 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DSPK1>;
+				clock-names = "dspk";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DSPK1>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <12288000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DSPK1";
+				status = "disabled";
+			};
+
+			tegra_dspk2: dspk@2905100 {
+				compatible = "nvidia,tegra186-dspk";
+				reg = <0x2905100 0x100>;
+				clocks = <&bpmp TEGRA186_CLK_DSPK2>;
+				clock-names = "dspk";
+				assigned-clocks = <&bpmp TEGRA186_CLK_DSPK2>;
+				assigned-clock-parents = <&bpmp TEGRA186_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <12288000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DSPK2";
+				status = "disabled";
+			};
+		};
 	};
 
 	memory-controller@2c00000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index ccac43b..7da39f0 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -80,7 +80,7 @@
 			ranges = <0x02900000 0x02900000 0x200000>;
 			status = "disabled";
 
-			dma-controller@2930000 {
+			adma: dma-controller@2930000 {
 				compatible = "nvidia,tegra194-adma",
 					     "nvidia,tegra186-adma";
 				reg = <0x02930000 0x20000>;
@@ -137,6 +137,243 @@
 				clock-names = "clk";
 				status = "disabled";
 			};
+
+			tegra_ahub: ahub@2900800 {
+				compatible = "nvidia,tegra194-ahub",
+					     "nvidia,tegra186-ahub";
+				reg = <0x02900800 0x800>;
+				clocks = <&bpmp TEGRA194_CLK_AHUB>;
+				clock-names = "ahub";
+				assigned-clocks = <&bpmp TEGRA194_CLK_AHUB>;
+				assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x02900800 0x02900800 0x11800>;
+				#sound-dai-cells = <1>;
+				status = "disabled";
+
+				tegra_admaif: admaif@290f000 {
+					compatible = "nvidia,tegra194-admaif",
+						     "nvidia,tegra186-admaif";
+					reg = <0x0290f000 0x1000>;
+					dmas = <&adma 1>, <&adma 1>,
+					       <&adma 2>, <&adma 2>,
+					       <&adma 3>, <&adma 3>,
+					       <&adma 4>, <&adma 4>,
+					       <&adma 5>, <&adma 5>,
+					       <&adma 6>, <&adma 6>,
+					       <&adma 7>, <&adma 7>,
+					       <&adma 8>, <&adma 8>,
+					       <&adma 9>, <&adma 9>,
+					       <&adma 10>, <&adma 10>,
+					       <&adma 11>, <&adma 11>,
+					       <&adma 12>, <&adma 12>,
+					       <&adma 13>, <&adma 13>,
+					       <&adma 14>, <&adma 14>,
+					       <&adma 15>, <&adma 15>,
+					       <&adma 16>, <&adma 16>,
+					       <&adma 17>, <&adma 17>,
+					       <&adma 18>, <&adma 18>,
+					       <&adma 19>, <&adma 19>,
+					       <&adma 20>, <&adma 20>;
+					dma-names = "rx1", "tx1",
+						    "rx2", "tx2",
+						    "rx3", "tx3",
+						    "rx4", "tx4",
+						    "rx5", "tx5",
+						    "rx6", "tx6",
+						    "rx7", "tx7",
+						    "rx8", "tx8",
+						    "rx9", "tx9",
+						    "rx10", "tx10",
+						    "rx11", "tx11",
+						    "rx12", "tx12",
+						    "rx13", "tx13",
+						    "rx14", "tx14",
+						    "rx15", "tx15",
+						    "rx16", "tx16",
+						    "rx17", "tx17",
+						    "rx18", "tx18",
+						    "rx19", "tx19",
+						    "rx20", "tx20";
+					#sound-dai-cells = <1>;
+					status = "disabled";
+				};
+
+				tegra_i2s1: i2s@2901000 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901000 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S1>,
+						 <&bpmp TEGRA194_CLK_I2S1_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S1>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S1";
+					status = "disabled";
+				};
+
+				tegra_i2s2: i2s@2901100 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901100 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S2>,
+						 <&bpmp TEGRA194_CLK_I2S2_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S2>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S2";
+					status = "disabled";
+				};
+
+				tegra_i2s3: i2s@2901200 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901200 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S3>,
+						 <&bpmp TEGRA194_CLK_I2S3_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S3>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S3";
+					status = "disabled";
+				};
+
+				tegra_i2s4: i2s@2901300 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901300 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S4>,
+						 <&bpmp TEGRA194_CLK_I2S4_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S4>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S4";
+					status = "disabled";
+				};
+
+				tegra_i2s5: i2s@2901400 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901400 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S5>,
+						 <&bpmp TEGRA194_CLK_I2S5_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S5>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S5";
+					status = "disabled";
+				};
+
+				tegra_i2s6: i2s@2901500 {
+					compatible = "nvidia,tegra194-i2s",
+						     "nvidia,tegra210-i2s";
+					reg = <0x2901500 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_I2S6>,
+						 <&bpmp TEGRA194_CLK_I2S6_SYNC_INPUT>;
+					clock-names = "i2s", "sync_input";
+					assigned-clocks = <&bpmp TEGRA194_CLK_I2S6>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <1536000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "I2S6";
+					status = "disabled";
+				};
+
+				tegra_dmic1: dmic@2904000 {
+					compatible = "nvidia,tegra194-dmic",
+						     "nvidia,tegra210-dmic";
+					reg = <0x2904000 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DMIC1>;
+					clock-names = "dmic";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DMIC1>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <3072000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DMIC1";
+					status = "disabled";
+				};
+
+				tegra_dmic2: dmic@2904100 {
+					compatible = "nvidia,tegra194-dmic",
+						     "nvidia,tegra210-dmic";
+					reg = <0x2904100 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DMIC2>;
+					clock-names = "dmic";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DMIC2>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <3072000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DMIC2";
+					status = "disabled";
+				};
+
+				tegra_dmic3: dmic@2904200 {
+					compatible = "nvidia,tegra194-dmic",
+						     "nvidia,tegra210-dmic";
+					reg = <0x2904200 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DMIC3>;
+					clock-names = "dmic";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DMIC3>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <3072000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DMIC3";
+					status = "disabled";
+				};
+
+				tegra_dmic4: dmic@2904300 {
+					compatible = "nvidia,tegra194-dmic",
+						     "nvidia,tegra210-dmic";
+					reg = <0x2904300 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DMIC4>;
+					clock-names = "dmic";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DMIC4>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <3072000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DMIC4";
+					status = "disabled";
+				};
+
+				tegra_dspk1: dspk@2905000 {
+					compatible = "nvidia,tegra194-dspk",
+						     "nvidia,tegra186-dspk";
+					reg = <0x2905000 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DSPK1>;
+					clock-names = "dspk";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DSPK1>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <12288000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DSPK1";
+					status = "disabled";
+				};
+
+				tegra_dspk2: dspk@2905100 {
+					compatible = "nvidia,tegra194-dspk",
+						     "nvidia,tegra186-dspk";
+					reg = <0x2905100 0x100>;
+					clocks = <&bpmp TEGRA194_CLK_DSPK2>;
+					clock-names = "dspk";
+					assigned-clocks = <&bpmp TEGRA194_CLK_DSPK2>;
+					assigned-clock-parents = <&bpmp TEGRA194_CLK_PLLA_OUT0>;
+					assigned-clock-rates = <12288000>;
+					#sound-dai-cells = <1>;
+					sound-name-prefix = "DSPK2";
+					status = "disabled";
+				};
+			};
 		};
 
 		pinmux: pinmux@2430000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 48c6325..98f32aa 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1288,6 +1288,151 @@
 			clock-names = "clk";
 			status = "disabled";
 		};
+
+		tegra_ahub: ahub@702d0800 {
+			compatible = "nvidia,tegra210-ahub";
+			reg = <0x702d0800 0x800>;
+			clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+			clock-names = "ahub";
+			assigned-clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+			assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x702d0000 0x702d0000 0x0000e400>;
+			#sound-dai-cells = <1>;
+			status = "disabled";
+
+			tegra_admaif: admaif@702d0000 {
+				compatible = "nvidia,tegra210-admaif";
+				reg = <0x702d0000 0x800>;
+				dmas = <&adma 1>,  <&adma 1>,
+				       <&adma 2>,  <&adma 2>,
+				       <&adma 3>,  <&adma 3>,
+				       <&adma 4>,  <&adma 4>,
+				       <&adma 5>,  <&adma 5>,
+				       <&adma 6>,  <&adma 6>,
+				       <&adma 7>,  <&adma 7>,
+				       <&adma 8>,  <&adma 8>,
+				       <&adma 9>,  <&adma 9>,
+				       <&adma 10>, <&adma 10>;
+				dma-names = "rx1",  "tx1",
+					    "rx2",  "tx2",
+					    "rx3",  "tx3",
+					    "rx4",  "tx4",
+					    "rx5",  "tx5",
+					    "rx6",  "tx6",
+					    "rx7",  "tx7",
+					    "rx8",  "tx8",
+					    "rx9",  "tx9",
+					    "rx10", "tx10";
+				#sound-dai-cells = <1>;
+				status = "disabled";
+			};
+
+			tegra_i2s1: i2s@702d1000 {
+				compatible = "nvidia,tegra210-i2s";
+				reg = <0x702d1000 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+				clock-names = "i2s";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S1";
+				status = "disabled";
+			};
+
+			tegra_i2s2: i2s@702d1100 {
+				compatible = "nvidia,tegra210-i2s";
+				reg = <0x702d1100 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_I2S1>;
+				clock-names = "i2s";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_I2S1>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S2";
+				status = "disabled";
+			};
+
+			tegra_i2s3: i2s@702d1200 {
+				compatible = "nvidia,tegra210-i2s";
+				reg = <0x702d1200 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_I2S2>;
+				clock-names = "i2s";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_I2S2>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S3";
+				status = "disabled";
+			};
+
+			tegra_i2s4: i2s@702d1300 {
+				compatible = "nvidia,tegra210-i2s";
+				reg = <0x702d1300 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_I2S3>;
+				clock-names = "i2s";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_I2S3>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S4";
+				status = "disabled";
+			};
+
+			tegra_i2s5: i2s@702d1400 {
+				compatible = "nvidia,tegra210-i2s";
+				reg = <0x702d1400 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_I2S4>;
+				clock-names = "i2s";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_I2S4>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <1536000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "I2S5";
+				status = "disabled";
+			};
+
+			tegra_dmic1: dmic@702d4000 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x702d4000 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_DMIC1>;
+				clock-names = "dmic";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_DMIC1>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC1";
+				status = "disabled";
+			};
+
+			tegra_dmic2: dmic@702d4100 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x702d4100 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_DMIC2>;
+				clock-names = "dmic";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_DMIC2>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC2";
+				status = "disabled";
+			};
+
+			tegra_dmic3: dmic@702d4200 {
+				compatible = "nvidia,tegra210-dmic";
+				reg = <0x702d4200 0x100>;
+				clocks = <&tegra_car TEGRA210_CLK_DMIC3>;
+				clock-names = "dmic";
+				assigned-clocks = <&tegra_car TEGRA210_CLK_DMIC3>;
+				assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+				assigned-clock-rates = <3072000>;
+				#sound-dai-cells = <1>;
+				sound-name-prefix = "DMIC3";
+				status = "disabled";
+			};
+		};
 	};
 
 	spi@70410000 {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 9/9] arm64: tegra: enable AHUB modules for few Tegra chips
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (7 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 8/9] arm64: tegra: add AHUB components for few Tegra chips Sameer Pujar
@ 2020-01-20 14:23 ` Sameer Pujar
  2020-01-28 10:49 ` [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-20 14:23 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: broonie, lgirdwood, thierry.reding, jonathanh, alsa-devel,
	devicetree, linux-tegra, linux-kernel, sharadg, mkumard,
	viswanathl, rlokhande, dramesh, atalambedu, Sameer Pujar

This patch enables AHUB, ADMAIF modules for following Tegra platforms.
Along with this specific instances of I/O modules are enabled as per
the board design.

 * Jetson TX1
   - I2S1, I2S2, I2S3, I2S4 and I2S5
   - DMIC1, DMIC2 and DMIC3

 * Jetson TX2
   - I2S1, I2S2, I2S3, I2S4, I2S5 and I2S6
   - DMIC1, DMIC2 and DMIC3
   - DSPK2

 * Jetson AGX Xavier
   - I2S1, I2S2, I2S4 and I2S6
   - DMIC2 and DMIC3
   - DSPK1

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 48 ++++++++++++++++++++++
 arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts | 36 ++++++++++++++++
 arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts | 40 ++++++++++++++++++
 3 files changed, 124 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
index f1de4ff..717993c 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
@@ -20,6 +20,54 @@
 		interrupt-controller@2a40000 {
 			status = "okay";
 		};
+
+		ahub@2900800 {
+			status = "okay";
+
+			admaif@290f000 {
+				status = "okay";
+			};
+
+			i2s@2901000 {
+				status = "okay";
+			};
+
+			i2s@2901100 {
+				status = "okay";
+			};
+
+			i2s@2901200 {
+				status = "okay";
+			};
+
+			i2s@2901300 {
+				status = "okay";
+			};
+
+			i2s@2901400 {
+				status = "okay";
+			};
+
+			i2s@2901500 {
+				status = "okay";
+			};
+
+			dmic@2904000 {
+				status = "okay";
+			};
+
+			dmic@2904100 {
+				status = "okay";
+			};
+
+			dmic@2904200 {
+				status = "okay";
+			};
+
+			dspk@2905100 {
+				status = "okay";
+			};
+		};
 	};
 
 	i2c@3160000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts b/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts
index 985e7d8..f5d5832 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts
@@ -21,6 +21,42 @@
 			interrupt-controller@2a40000 {
 				status = "okay";
 			};
+
+			ahub@2900800 {
+				status = "okay";
+
+				admaif@290f000 {
+					status = "okay";
+				};
+
+				i2s@2901000 {
+					status = "okay";
+				};
+
+				i2s@2901100 {
+					status = "okay";
+				};
+
+				i2s@2901300 {
+					status = "okay";
+				};
+
+				i2s@2901500 {
+					status = "okay";
+				};
+
+				dmic@2904100 {
+					status = "okay";
+				};
+
+				dmic@2904200 {
+					status = "okay";
+				};
+
+				dspk@2905000 {
+					status = "okay";
+				};
+			};
 		};
 
 		ddc: i2c@31c0000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts b/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts
index a3cafe3..c8d2c21 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts
@@ -123,5 +123,45 @@
 		agic@702f9000 {
 			status = "okay";
 		};
+
+		ahub@702d0800 {
+			status = "okay";
+
+			admaif@702d0000 {
+				status = "okay";
+			};
+
+			i2s@702d1000 {
+				status = "okay";
+			};
+
+			i2s@702d1100 {
+				status = "okay";
+			};
+
+			i2s@702d1200 {
+				status = "okay";
+			};
+
+			i2s@702d1300 {
+				status = "okay";
+			};
+
+			i2s@702d1400 {
+				status = "okay";
+			};
+
+			dmic@702d4000 {
+				status = "okay";
+			};
+
+			dmic@702d4100 {
+				status = "okay";
+			};
+
+			dmic@702d4200 {
+				status = "okay";
+			};
+		};
 	};
 };
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-20 14:23 ` [PATCH 2/9] ASoC: tegra: add support for CIF programming Sameer Pujar
@ 2020-01-20 15:58   ` Dmitry Osipenko
  2020-01-21  4:41     ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-20 15:58 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

Hello Sameer,

20.01.2020 17:23, Sameer Pujar пишет:

[snip]

> Tegra30 and Tegra124 have an identical CIF programming helper function.

[snip]

> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT	24
> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US	0x3f
> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK	(TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
> -
> -/* Channel count minus 1 */
> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT	24
> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US	7
> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)

The AUDIOCIF_CTRL bitfields are not the same on T30 and T124, why are
you claiming that programming is identical? Have you actually tried to
test these patches on T30?

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-20 15:58   ` [alsa-devel] " Dmitry Osipenko
@ 2020-01-21  4:41     ` Sameer Pujar
  2020-01-21 16:04       ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-21  4:41 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh


On 1/20/2020 9:28 PM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sameer,
>
> 20.01.2020 17:23, Sameer Pujar пишет:
>
> [snip]
>
>> Tegra30 and Tegra124 have an identical CIF programming helper function.
> [snip]
>
>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT  24
>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US        0x3f
>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK   (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
>> -
>> -/* Channel count minus 1 */
>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT   24
>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7
>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK    (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
> The AUDIOCIF_CTRL bitfields are not the same on T30 and T124, why are
> you claiming that programming is identical? Have you actually tried to
> test these patches on T30?

Oh yes! seems like I overlooked the macro values. Thanks for pointing 
this. I will retain separate CIF function for Tegra30.
I do not have a Tegra30 board with me and hence could not test anything 
specific to it apart from build sanity.
If someone can help me test I would really appreciate.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-20 14:23 ` [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver Sameer Pujar
@ 2020-01-21  5:15   ` Dmitry Osipenko
  2020-01-21 14:21     ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-21  5:15 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

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@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.

> +#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.

> +	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.

> +{
> +	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.

> +	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?

> +	.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.

> +	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 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.

> +	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.

> +		.owner = THIS_MODULE,

No need to explicitly set the owner, drivers core takes care about it.

> +		.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@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?

> 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?

> +	bool loopback;
> +};
> +
> +#endif
> 


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-21  5:15   ` [alsa-devel] " Dmitry Osipenko
@ 2020-01-21 14:21     ` Sameer Pujar
  2020-01-21 16:03       ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-21 14:21 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh



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@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@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
>>


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-21 14:21     ` Sameer Pujar
@ 2020-01-21 16:03       ` Dmitry Osipenko
  2020-01-22  4:32         ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-21 16:03 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

21.01.2020 17:21, Sameer Pujar пишет:

[snip]

>>> +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.
> -----

Using 'checkpatch --strict':

CHECK: Alignment should match open parenthesis
#2693: FILE: sound/soc/tegra/tegra210_i2s.c:362:
+static int tegra210_i2s_put_control(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol

[snip]

>>> +
>>> +     } 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

CHECK: braces {} should be used on all arms of this statement
#2699: FILE: sound/soc/tegra/tegra210_i2s.c:368:
+       if (strstr(kcontrol->id.name, "Loopback")) {
[...]
+       } else if (strstr(kcontrol->id.name, "Sample Rate"))
[...]
+       else if (strstr(kcontrol->id.name, "FSYNC Width")) {
[...]
+       } else if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
[...]
+       else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
[...]
+       else if (strstr(kcontrol->id.name, "Client Bit Format"))
[...]
+       else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
[...]
+       else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
[...]
+       else if (strstr(kcontrol->id.name, "Client Channels"))
[...]
+       else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
[...]
+       else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
[...]
+       else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
[...]
+       else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
[...]
+       else if (strstr(kcontrol->id.name, "Playback FIFO Threshold"))
[...]
+       else if (strstr(kcontrol->id.name, "BCLK Ratio"))
[...]

[snip]

>>> +     pm_runtime_enable(dev);
>> Error checking?
> 
> return type for above is void()

Ok

>>> +     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.

It should matter (if I'm not missing something) because RPM should be in
a wrecked state once you'll try to re-load the driver's module. Likely
that those few other drivers are wrong.

[snip]

> 
>>> +     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"
Explicit typecasting isn't needed for integers.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-21  4:41     ` Sameer Pujar
@ 2020-01-21 16:04       ` Dmitry Osipenko
  2020-01-27  5:11         ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-21 16:04 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

21.01.2020 07:41, Sameer Pujar пишет:
> 
> On 1/20/2020 9:28 PM, Dmitry Osipenko wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> Hello Sameer,
>>
>> 20.01.2020 17:23, Sameer Pujar пишет:
>>
>> [snip]
>>
>>> Tegra30 and Tegra124 have an identical CIF programming helper function.
>> [snip]
>>
>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT  24
>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US        0x3f
>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK  
>>> (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US <<
>>> TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
>>> -
>>> -/* Channel count minus 1 */
>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT   24
>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7
>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK   
>>> (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US <<
>>> TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
>> The AUDIOCIF_CTRL bitfields are not the same on T30 and T124, why are
>> you claiming that programming is identical? Have you actually tried to
>> test these patches on T30?
> 
> Oh yes! seems like I overlooked the macro values. Thanks for pointing
> this. I will retain separate CIF function for Tegra30.
> I do not have a Tegra30 board with me and hence could not test anything
> specific to it apart from build sanity.
> If someone can help me test I would really appreciate.
> 

I'll help with the testing once all obvious problems will be fixed.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-21 16:03       ` Dmitry Osipenko
@ 2020-01-22  4:32         ` Sameer Pujar
  2020-01-22  6:23           ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-22  4:32 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh



On 1/21/2020 9:33 PM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 21.01.2020 17:21, Sameer Pujar пишет:
>
> [snip]
>
>>>> +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.
>> -----
> Using 'checkpatch --strict':
>
> CHECK: Alignment should match open parenthesis
> #2693: FILE: sound/soc/tegra/tegra210_i2s.c:362:
> +static int tegra210_i2s_put_control(struct snd_kcontrol *kcontrol,
> +       struct snd_ctl_elem_value *ucontrol
>
> [snip]

will fix
>>>> +
>>>> +     } 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
> CHECK: braces {} should be used on all arms of this statement
> #2699: FILE: sound/soc/tegra/tegra210_i2s.c:368:
> +       if (strstr(kcontrol->id.name, "Loopback")) {
> [...]
> +       } else if (strstr(kcontrol->id.name, "Sample Rate"))
> [...]
> +       else if (strstr(kcontrol->id.name, "FSYNC Width")) {
> [...]
> +       } else if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Client Bit Format"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Client Channels"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Capture Stereo To Mono"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Capture Mono To Stereo"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Playback Stereo To Mono"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Playback Mono To Stereo"))
> [...]
> +       else if (strstr(kcontrol->id.name, "Playback FIFO Threshold"))
> [...]
> +       else if (strstr(kcontrol->id.name, "BCLK Ratio"))
> [...]
>
> [snip]

will fix
>>>> +     pm_runtime_enable(dev);
>>> Error checking?
>> return type for above is void()
> Ok
>
>>>> +     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.
> It should matter (if I'm not missing something) because RPM should be in
> a wrecked state once you'll try to re-load the driver's module. Likely
> that those few other drivers are wrong.
>
> [snip]

Once the driver is re-loaded and RPM is enabled, I don't think it would use
the same 'dev' and the corresponding ref count. Doesn't it use the new 
counters?
If RPM is not working for some reason, most likely it would be the case 
for other
devices. What best driver can do is probably do a force suspend during 
removal if
already not done. I would prefer to keep, since multiple drivers still 
have it,
unless there is a real harm in doing so.
>
>>>> +     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"
> Explicit typecasting isn't needed for integers.

What I meant was, rx_fifo_th is checked against a 'int' variable in an 
"if" condition.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22  4:32         ` Sameer Pujar
@ 2020-01-22  6:23           ` Dmitry Osipenko
  2020-01-22  7:16             ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-22  6:23 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

22.01.2020 07:32, Sameer Pujar пишет:
[snip]
>>>>> +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.
>> It should matter (if I'm not missing something) because RPM should be in
>> a wrecked state once you'll try to re-load the driver's module. Likely
>> that those few other drivers are wrong.
>>
>> [snip]
> 
> Once the driver is re-loaded and RPM is enabled, I don't think it would use
> the same 'dev' and the corresponding ref count. Doesn't it use the new
> counters?
> If RPM is not working for some reason, most likely it would be the case
> for other
> devices. What best driver can do is probably do a force suspend during
> removal if
> already not done. I would prefer to keep, since multiple drivers still
> have it,
> unless there is a real harm in doing so.

I took a closer look and looks like the counter actually should be
reset. Still I don't think that it's a good practice to make changes
underneath of RPM, it may strike back.

>>>>> +     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"
>> Explicit typecasting isn't needed for integers.
> 
> What I meant was, rx_fifo_th is checked against a 'int' variable in an
> "if" condition.

What's the problem with comparing of unsigned with signed?

Besides, cif_conf.audio_ch > I2S_RX_FIFO_DEPTH can't be ever true, isn't
it? I2S_RX_FIFO_DEPTH=64, channels_max=16

Lastly, nothing stops you to make max_th unsigned.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22  6:23           ` Dmitry Osipenko
@ 2020-01-22  7:16             ` Sameer Pujar
  2020-01-22 11:52               ` Jon Hunter
  2020-01-22 16:26               ` Dmitry Osipenko
  0 siblings, 2 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-22  7:16 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, jonathanh, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh



On 1/22/2020 11:53 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 22.01.2020 07:32, Sameer Pujar пишет:
> [snip]
>>>>>> +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.
>>> It should matter (if I'm not missing something) because RPM should be in
>>> a wrecked state once you'll try to re-load the driver's module. Likely
>>> that those few other drivers are wrong.
>>>
>>> [snip]
>> Once the driver is re-loaded and RPM is enabled, I don't think it would use
>> the same 'dev' and the corresponding ref count. Doesn't it use the new
>> counters?
>> If RPM is not working for some reason, most likely it would be the case
>> for other
>> devices. What best driver can do is probably do a force suspend during
>> removal if
>> already not done. I would prefer to keep, since multiple drivers still
>> have it,
>> unless there is a real harm in doing so.
> I took a closer look and looks like the counter actually should be
> reset. Still I don't think that it's a good practice to make changes
> underneath of RPM, it may strike back.

If RPM is broken, it probably would have been caught during device usage.
I will remove explicit suspend here if no any concerns from other folks. 
Thanks.
>
>>>>>> +     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"
>>> Explicit typecasting isn't needed for integers.
>> What I meant was, rx_fifo_th is checked against a 'int' variable in an
>> "if" condition.
> What's the problem with comparing of unsigned with signed?

consider this example,
----
unsigned int x = 5;
int y = -1;

(x > y) is false.
----
Hence should be careful while using signed and unsigned comparisons.
>
> Besides, cif_conf.audio_ch > I2S_RX_FIFO_DEPTH can't be ever true, isn't
> it? I2S_RX_FIFO_DEPTH=64, channels_max=16

Yes true.
> Lastly, nothing stops you to make max_th unsigned.

will update.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22  7:16             ` Sameer Pujar
@ 2020-01-22 11:52               ` Jon Hunter
  2020-01-22 16:27                 ` Dmitry Osipenko
  2020-01-22 16:26               ` Dmitry Osipenko
  1 sibling, 1 reply; 43+ messages in thread
From: Jon Hunter @ 2020-01-22 11:52 UTC (permalink / raw)
  To: Sameer Pujar, Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	viswanathl, sharadg, broonie, thierry.reding, linux-tegra,
	rlokhande, mkumard, dramesh


On 22/01/2020 07:16, Sameer Pujar wrote:

...

>>>>>>> +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.
>>>> It should matter (if I'm not missing something) because RPM should
>>>> be in
>>>> a wrecked state once you'll try to re-load the driver's module. Likely
>>>> that those few other drivers are wrong.
>>>>
>>>> [snip]
>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>> would use
>>> the same 'dev' and the corresponding ref count. Doesn't it use the new
>>> counters?
>>> If RPM is not working for some reason, most likely it would be the case
>>> for other
>>> devices. What best driver can do is probably do a force suspend during
>>> removal if
>>> already not done. I would prefer to keep, since multiple drivers still
>>> have it,
>>> unless there is a real harm in doing so.
>> I took a closer look and looks like the counter actually should be
>> reset. Still I don't think that it's a good practice to make changes
>> underneath of RPM, it may strike back.
> 
> If RPM is broken, it probably would have been caught during device usage.
> I will remove explicit suspend here if no any concerns from other folks.
> Thanks.

I recall that this was the preferred way of doing this from the RPM
folks. Tegra30 I2S driver does the same and Stephen had pointed me to
this as a reference. I believe that this is meant to ensure that the
device is always powered-off regardless of it RPM is enabled or not and
what the current state is.

Now for Tegra210 (or actually 64-bit Tegra) RPM is always enabled and so
we don't need to worry about the !RPM case. However, I still don't see
the harm in this.

Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22  7:16             ` Sameer Pujar
  2020-01-22 11:52               ` Jon Hunter
@ 2020-01-22 16:26               ` Dmitry Osipenko
  1 sibling, 0 replies; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-22 16:26 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

22.01.2020 10:16, Sameer Pujar пишет:
> 
> 
> On 1/22/2020 11:53 AM, Dmitry Osipenko wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> 22.01.2020 07:32, Sameer Pujar пишет:
>> [snip]
>>>>>>> +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.
>>>> It should matter (if I'm not missing something) because RPM should
>>>> be in
>>>> a wrecked state once you'll try to re-load the driver's module. Likely
>>>> that those few other drivers are wrong.
>>>>
>>>> [snip]
>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>> would use
>>> the same 'dev' and the corresponding ref count. Doesn't it use the new
>>> counters?
>>> If RPM is not working for some reason, most likely it would be the case
>>> for other
>>> devices. What best driver can do is probably do a force suspend during
>>> removal if
>>> already not done. I would prefer to keep, since multiple drivers still
>>> have it,
>>> unless there is a real harm in doing so.
>> I took a closer look and looks like the counter actually should be
>> reset. Still I don't think that it's a good practice to make changes
>> underneath of RPM, it may strike back.
> 
> If RPM is broken, it probably would have been caught during device usage.
> I will remove explicit suspend here if no any concerns from other folks.
> Thanks.
>>
>>>>>>> +     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"
>>>> Explicit typecasting isn't needed for integers.
>>> What I meant was, rx_fifo_th is checked against a 'int' variable in an
>>> "if" condition.
>> What's the problem with comparing of unsigned with signed?
> 
> consider this example,
> ----
> unsigned int x = 5;
> int y = -1;
> 
> (x > y) is false.

Right

> ----
> Hence should be careful while using signed and unsigned comparisons.
>>
>> Besides, cif_conf.audio_ch > I2S_RX_FIFO_DEPTH can't be ever true, isn't
>> it? I2S_RX_FIFO_DEPTH=64, channels_max=16
> 
> Yes true.
>> Lastly, nothing stops you to make max_th unsigned.
> 
> will update.
> 

Thanks

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22 11:52               ` Jon Hunter
@ 2020-01-22 16:27                 ` Dmitry Osipenko
  2020-01-23  9:22                   ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-22 16:27 UTC (permalink / raw)
  To: Jon Hunter, Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	viswanathl, sharadg, broonie, thierry.reding, linux-tegra,
	rlokhande, mkumard, dramesh

22.01.2020 14:52, Jon Hunter пишет:
> 
> On 22/01/2020 07:16, Sameer Pujar wrote:
> 
> ...
> 
>>>>>>>> +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.
>>>>> It should matter (if I'm not missing something) because RPM should
>>>>> be in
>>>>> a wrecked state once you'll try to re-load the driver's module. Likely
>>>>> that those few other drivers are wrong.
>>>>>
>>>>> [snip]
>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>> would use
>>>> the same 'dev' and the corresponding ref count. Doesn't it use the new
>>>> counters?
>>>> If RPM is not working for some reason, most likely it would be the case
>>>> for other
>>>> devices. What best driver can do is probably do a force suspend during
>>>> removal if
>>>> already not done. I would prefer to keep, since multiple drivers still
>>>> have it,
>>>> unless there is a real harm in doing so.
>>> I took a closer look and looks like the counter actually should be
>>> reset. Still I don't think that it's a good practice to make changes
>>> underneath of RPM, it may strike back.
>>
>> If RPM is broken, it probably would have been caught during device usage.
>> I will remove explicit suspend here if no any concerns from other folks.
>> Thanks.
> 
> I recall that this was the preferred way of doing this from the RPM
> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
> this as a reference.

> I believe that this is meant to ensure that the
> device is always powered-off regardless of it RPM is enabled or not and
> what the current state is.

Yes, it was kinda actual for the case of unavailable RPM.

Anyways, /I think/ variant like this should have been more preferred:

if (!pm_runtime_enabled(&pdev->dev))
	tegra210_i2s_runtime_suspend(&pdev->dev);
else
	pm_runtime_disable(&pdev->dev);

> Now for Tegra210 (or actually 64-bit Tegra) RPM is always enabled and so
> we don't need to worry about the !RPM case. However, I still don't see
> the harm in this.

There is no real harm today, but:

1. I'd prefer to be very careful with RPM in general, based on
   previous experience.

2. It should be a bug if device isn't RPM-suspended during
   of driver's removal. Thus the real problem needs to be fixed
   rather than worked around.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-22 16:27                 ` Dmitry Osipenko
@ 2020-01-23  9:22                   ` Sameer Pujar
  2020-01-23 15:16                     ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-23  9:22 UTC (permalink / raw)
  To: Dmitry Osipenko, Jon Hunter, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh



On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 22.01.2020 14:52, Jon Hunter пишет:
>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>
>> ...
>>
>>>>>>>>> +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.
>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>> be in
>>>>>> a wrecked state once you'll try to re-load the driver's module. Likely
>>>>>> that those few other drivers are wrong.
>>>>>>
>>>>>> [snip]
>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>> would use
>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the new
>>>>> counters?
>>>>> If RPM is not working for some reason, most likely it would be the case
>>>>> for other
>>>>> devices. What best driver can do is probably do a force suspend during
>>>>> removal if
>>>>> already not done. I would prefer to keep, since multiple drivers still
>>>>> have it,
>>>>> unless there is a real harm in doing so.
>>>> I took a closer look and looks like the counter actually should be
>>>> reset. Still I don't think that it's a good practice to make changes
>>>> underneath of RPM, it may strike back.
>>> If RPM is broken, it probably would have been caught during device usage.
>>> I will remove explicit suspend here if no any concerns from other folks.
>>> Thanks.
>> I recall that this was the preferred way of doing this from the RPM
>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>> this as a reference.
>> I believe that this is meant to ensure that the
>> device is always powered-off regardless of it RPM is enabled or not and
>> what the current state is.
> Yes, it was kinda actual for the case of unavailable RPM.

> Anyways, /I think/ variant like this should have been more preferred:
>
> if (!pm_runtime_enabled(&pdev->dev))
>          tegra210_i2s_runtime_suspend(&pdev->dev);
> else
>          pm_runtime_disable(&pdev->dev);

I think it looks to be similar to what is there already.

pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call 
if !RPM
if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
         tegra210_i2s_runtime_suspend(&pdev->dev);
>> Now for Tegra210 (or actually 64-bit Tegra) RPM is always enabled and so
>> we don't need to worry about the !RPM case. However, I still don't see
>> the harm in this.
> There is no real harm today, but:
>
> 1. I'd prefer to be very careful with RPM in general, based on
>     previous experience.
>
> 2. It should be a bug if device isn't RPM-suspended during
>     of driver's removal. Thus the real problem needs to be fixed
>     rather than worked around.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-23  9:22                   ` Sameer Pujar
@ 2020-01-23 15:16                     ` Dmitry Osipenko
  2020-01-24  9:07                       ` Jon Hunter
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-23 15:16 UTC (permalink / raw)
  To: Sameer Pujar, Jon Hunter
  Cc: perex, tiwai, robh+dt, devicetree, alsa-devel, atalambedu,
	linux-kernel, lgirdwood, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh

23.01.2020 12:22, Sameer Pujar пишет:
> 
> 
> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> 22.01.2020 14:52, Jon Hunter пишет:
>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>
>>> ...
>>>
>>>>>>>>>> +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.
>>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>>> be in
>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>> Likely
>>>>>>> that those few other drivers are wrong.
>>>>>>>
>>>>>>> [snip]
>>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>>> would use
>>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the
>>>>>> new
>>>>>> counters?
>>>>>> If RPM is not working for some reason, most likely it would be the
>>>>>> case
>>>>>> for other
>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>> during
>>>>>> removal if
>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>> still
>>>>>> have it,
>>>>>> unless there is a real harm in doing so.
>>>>> I took a closer look and looks like the counter actually should be
>>>>> reset. Still I don't think that it's a good practice to make changes
>>>>> underneath of RPM, it may strike back.
>>>> If RPM is broken, it probably would have been caught during device
>>>> usage.
>>>> I will remove explicit suspend here if no any concerns from other
>>>> folks.
>>>> Thanks.
>>> I recall that this was the preferred way of doing this from the RPM
>>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>>> this as a reference.
>>> I believe that this is meant to ensure that the
>>> device is always powered-off regardless of it RPM is enabled or not and
>>> what the current state is.
>> Yes, it was kinda actual for the case of unavailable RPM.
> 
>> Anyways, /I think/ variant like this should have been more preferred:
>>
>> if (!pm_runtime_enabled(&pdev->dev))
>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>> else
>>          pm_runtime_disable(&pdev->dev);
> 
> I think it looks to be similar to what is there already.
> 
> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call
> if !RPM
> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
>         tegra210_i2s_runtime_suspend(&pdev->dev);

Maybe this is fine for !RPM, but not really fine in a case of enabled
RPM. Device could be in resumed state after pm_runtime_disable() if it
wasn't suspended before the disabling.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver
  2020-01-20 14:23 ` [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver Sameer Pujar
@ 2020-01-24  1:18   ` Dmitry Osipenko
  2020-01-24  3:39     ` Sameer Pujar
  2020-01-27  9:45     ` Jon Hunter
  0 siblings, 2 replies; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-24  1:18 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

20.01.2020 17:23, Sameer Pujar пишет:
[snip]
> +static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl,
> +				     struct snd_ctl_elem_value *uctl)
> +{
> +	struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
> +	struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
> +	struct soc_enum *e = (struct soc_enum *)kctl->private_value;
> +	unsigned int reg, i, bit_pos = 0;
> +
> +	/*
> +	 * Find the bit position of current MUX input.
> +	 * If nothing is set, position would be 0 and it corresponds to 'None'.
> +	 */
> +	for (i = 0; i < ahub->soc_data->reg_count; i++) {
> +		unsigned int reg_val;
> +
> +		reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
> +		snd_soc_component_read(cmpnt, reg, &reg_val);
> +		reg_val &= ahub->soc_data->mask[i];
> +
> +		if (reg_val) {
> +			bit_pos = ffs(reg_val) +
> +				  (8 * cmpnt->val_bytes * i);

Multiplication takes precedence, braces are not needed. Same for all
other occurrences in the code.

[snip]
> +			break;
> +		}
> +	}
> +
> +	/* Find index related to the item in array *_ahub_mux_texts[] */
> +	for (i = 0; i < e->items; i++) {
> +		if (bit_pos == e->values[i]) {
> +			uctl->value.enumerated.item[0] = i;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
> +				     struct snd_ctl_elem_value *uctl)
> +{
> +	struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
> +	struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
> +	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctl);
> +	struct soc_enum *e = (struct soc_enum *)kctl->private_value;
> +	struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { };

Shouldn't this be {0} to make array zero'ed?

[snip]
> +static int tegra_ahub_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct tegra_ahub *ahub;
> +	struct tegra_ahub_soc_data *soc_data;
> +	void __iomem *regs;
> +	struct resource *res;
> +	int ret;
> +
> +	match = of_match_device(tegra_ahub_of_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "error: no device match found\n");
> +		return -ENODEV;
> +	}
> +
> +	soc_data = (struct tegra_ahub_soc_data *)match->data;

soc_data = device_get_match_data(&pdev->dev);

> +	ahub = devm_kcalloc(&pdev->dev, 1, sizeof(*ahub), GFP_KERNEL);
> +	if (!ahub)
> +		return -ENOMEM;
> +
> +	ahub->soc_data = soc_data;
> +
> +	platform_set_drvdata(pdev, ahub);
> +
> +	ahub->clk = devm_clk_get(&pdev->dev, "ahub");
> +	if (IS_ERR(ahub->clk)) {
> +		dev_err(&pdev->dev, "can't retrieve AHUB clock\n");
> +		return PTR_ERR(ahub->clk);
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);

regs = devm_platform_ioremap_resource(pdev, 0);

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver
  2020-01-20 14:23 ` [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver Sameer Pujar
@ 2020-01-24  1:28   ` Dmitry Osipenko
  2020-01-24  3:27     ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-24  1:28 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

20.01.2020 17:23, Sameer Pujar пишет:
[snip]
> +static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
> +{
> +	struct tegra_admaif *admaif = dev_get_drvdata(dev);
> +	unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
> +	unsigned int num_ch = admaif->soc_data->num_ch;
> +	unsigned int rx_base = admaif->soc_data->rx_base;
> +	unsigned int tx_base = admaif->soc_data->tx_base;
> +	unsigned int global_base = admaif->soc_data->global_base;
> +	unsigned int reg_max = admaif->soc_data->regmap_conf->max_register;
> +	unsigned int rx_max = rx_base + (num_ch * ch_stride);
> +	unsigned int tx_max = tx_base + (num_ch * ch_stride);
> +
> +	if ((reg >= rx_base) && (reg < rx_max)) {

The braces are not needed around the comparisons because they precede
the AND. Same for all other similar occurrences in the code.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver
  2020-01-24  1:28   ` [alsa-devel] " Dmitry Osipenko
@ 2020-01-24  3:27     ` Sameer Pujar
  2020-01-24  4:25       ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-24  3:27 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, jonathanh, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh



On 1/24/2020 6:58 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 20.01.2020 17:23, Sameer Pujar пишет:
> [snip]
>> +static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
>> +{
>> +     struct tegra_admaif *admaif = dev_get_drvdata(dev);
>> +     unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
>> +     unsigned int num_ch = admaif->soc_data->num_ch;
>> +     unsigned int rx_base = admaif->soc_data->rx_base;
>> +     unsigned int tx_base = admaif->soc_data->tx_base;
>> +     unsigned int global_base = admaif->soc_data->global_base;
>> +     unsigned int reg_max = admaif->soc_data->regmap_conf->max_register;
>> +     unsigned int rx_max = rx_base + (num_ch * ch_stride);
>> +     unsigned int tx_max = tx_base + (num_ch * ch_stride);
>> +
>> +     if ((reg >= rx_base) && (reg < rx_max)) {
> The braces are not needed around the comparisons because they precede
> the AND. Same for all other similar occurrences in the code.

While that is true, some prefer to use explicit braces to make it more 
readable.
In the past I was told to use explicitly in such cases.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver
  2020-01-24  1:18   ` [alsa-devel] " Dmitry Osipenko
@ 2020-01-24  3:39     ` Sameer Pujar
  2020-01-24  4:28       ` Dmitry Osipenko
  2020-01-27  9:45     ` Jon Hunter
  1 sibling, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-24  3:39 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, jonathanh, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh



On 1/24/2020 6:48 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 20.01.2020 17:23, Sameer Pujar пишет:
> [snip]
>> +static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl,
>> +                                  struct snd_ctl_elem_value *uctl)
>> +{
>> +     struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
>> +     struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
>> +     struct soc_enum *e = (struct soc_enum *)kctl->private_value;
>> +     unsigned int reg, i, bit_pos = 0;
>> +
>> +     /*
>> +      * Find the bit position of current MUX input.
>> +      * If nothing is set, position would be 0 and it corresponds to 'None'.
>> +      */
>> +     for (i = 0; i < ahub->soc_data->reg_count; i++) {
>> +             unsigned int reg_val;
>> +
>> +             reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
>> +             snd_soc_component_read(cmpnt, reg, &reg_val);
>> +             reg_val &= ahub->soc_data->mask[i];
>> +
>> +             if (reg_val) {
>> +                     bit_pos = ffs(reg_val) +
>> +                               (8 * cmpnt->val_bytes * i);
> Multiplication takes precedence, braces are not needed. Same for all
> other occurrences in the code.
>
> [snip]
>> +                     break;
>> +             }
>> +     }
>> +
>> +     /* Find index related to the item in array *_ahub_mux_texts[] */
>> +     for (i = 0; i < e->items; i++) {
>> +             if (bit_pos == e->values[i]) {
>> +                     uctl->value.enumerated.item[0] = i;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
>> +                                  struct snd_ctl_elem_value *uctl)
>> +{
>> +     struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
>> +     struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
>> +     struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctl);
>> +     struct soc_enum *e = (struct soc_enum *)kctl->private_value;
>> +     struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { };
> Shouldn't this be {0} to make array zero'ed?

Isn't it the same with empty braces?
>
> [snip]
>> +static int tegra_ahub_probe(struct platform_device *pdev)
>> +{
>> +     const struct of_device_id *match;
>> +     struct tegra_ahub *ahub;
>> +     struct tegra_ahub_soc_data *soc_data;
>> +     void __iomem *regs;
>> +     struct resource *res;
>> +     int ret;
>> +
>> +     match = of_match_device(tegra_ahub_of_match, &pdev->dev);
>> +     if (!match) {
>> +             dev_err(&pdev->dev, "error: no device match found\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     soc_data = (struct tegra_ahub_soc_data *)match->data;
> soc_data = device_get_match_data(&pdev->dev);

will update
>> +     ahub = devm_kcalloc(&pdev->dev, 1, sizeof(*ahub), GFP_KERNEL);
>> +     if (!ahub)
>> +             return -ENOMEM;
>> +
>> +     ahub->soc_data = soc_data;
>> +
>> +     platform_set_drvdata(pdev, ahub);
>> +
>> +     ahub->clk = devm_clk_get(&pdev->dev, "ahub");
>> +     if (IS_ERR(ahub->clk)) {
>> +             dev_err(&pdev->dev, "can't retrieve AHUB clock\n");
>> +             return PTR_ERR(ahub->clk);
>> +     }
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +
>> +     regs = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(regs))
>> +             return PTR_ERR(regs);
> regs = devm_platform_ioremap_resource(pdev, 0);

will update


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver
  2020-01-24  3:27     ` Sameer Pujar
@ 2020-01-24  4:25       ` Dmitry Osipenko
  2020-01-27  5:08         ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-24  4:25 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

24.01.2020 06:27, Sameer Pujar пишет:
> 
> 
> On 1/24/2020 6:58 AM, Dmitry Osipenko wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> 20.01.2020 17:23, Sameer Pujar пишет:
>> [snip]
>>> +static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
>>> +{
>>> +     struct tegra_admaif *admaif = dev_get_drvdata(dev);
>>> +     unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
>>> +     unsigned int num_ch = admaif->soc_data->num_ch;
>>> +     unsigned int rx_base = admaif->soc_data->rx_base;
>>> +     unsigned int tx_base = admaif->soc_data->tx_base;
>>> +     unsigned int global_base = admaif->soc_data->global_base;
>>> +     unsigned int reg_max =
>>> admaif->soc_data->regmap_conf->max_register;
>>> +     unsigned int rx_max = rx_base + (num_ch * ch_stride);
>>> +     unsigned int tx_max = tx_base + (num_ch * ch_stride);
>>> +
>>> +     if ((reg >= rx_base) && (reg < rx_max)) {
>> The braces are not needed around the comparisons because they precede
>> the AND. Same for all other similar occurrences in the code.
> 
> While that is true, some prefer to use explicit braces to make it more
> readable.
> In the past I was told to use explicitly in such cases.

At least most of code in kernel (I've seen) doesn't have superfluous
parens (the curvy thingies actually should be the braces). Readability
is arguable in this case, I'm finding such code a bit more difficult to
read, although in some cases parens and spacing may help to read more
complex constructions.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver
  2020-01-24  3:39     ` Sameer Pujar
@ 2020-01-24  4:28       ` Dmitry Osipenko
  0 siblings, 0 replies; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-24  4:28 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

24.01.2020 06:39, Sameer Pujar пишет:
>>> +static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
>>> +                                  struct snd_ctl_elem_value *uctl)
>>> +{
>>> +     struct snd_soc_component *cmpnt =
>>> snd_soc_dapm_kcontrol_component(kctl);
>>> +     struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
>>> +     struct snd_soc_dapm_context *dapm =
>>> snd_soc_dapm_kcontrol_dapm(kctl);
>>> +     struct soc_enum *e = (struct soc_enum *)kctl->private_value;
>>> +     struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] =
>>> { };
>> Shouldn't this be {0} to make array zero'ed?
> 
> Isn't it the same with empty braces?

Looks like is should be the same because turns out GCC has an extension
for that.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-23 15:16                     ` Dmitry Osipenko
@ 2020-01-24  9:07                       ` Jon Hunter
  2020-01-24  9:51                         ` Jon Hunter
  0 siblings, 1 reply; 43+ messages in thread
From: Jon Hunter @ 2020-01-24  9:07 UTC (permalink / raw)
  To: Dmitry Osipenko, Sameer Pujar
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh


On 23/01/2020 15:16, Dmitry Osipenko wrote:
> 23.01.2020 12:22, Sameer Pujar пишет:
>>
>>
>> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>>> External email: Use caution opening links or attachments
>>>
>>>
>>> 22.01.2020 14:52, Jon Hunter пишет:
>>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>>
>>>> ...
>>>>
>>>>>>>>>>> +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.
>>>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>>>> be in
>>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>>> Likely
>>>>>>>> that those few other drivers are wrong.
>>>>>>>>
>>>>>>>> [snip]
>>>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>>>> would use
>>>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the
>>>>>>> new
>>>>>>> counters?
>>>>>>> If RPM is not working for some reason, most likely it would be the
>>>>>>> case
>>>>>>> for other
>>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>>> during
>>>>>>> removal if
>>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>>> still
>>>>>>> have it,
>>>>>>> unless there is a real harm in doing so.
>>>>>> I took a closer look and looks like the counter actually should be
>>>>>> reset. Still I don't think that it's a good practice to make changes
>>>>>> underneath of RPM, it may strike back.
>>>>> If RPM is broken, it probably would have been caught during device
>>>>> usage.
>>>>> I will remove explicit suspend here if no any concerns from other
>>>>> folks.
>>>>> Thanks.
>>>> I recall that this was the preferred way of doing this from the RPM
>>>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>>>> this as a reference.
>>>> I believe that this is meant to ensure that the
>>>> device is always powered-off regardless of it RPM is enabled or not and
>>>> what the current state is.
>>> Yes, it was kinda actual for the case of unavailable RPM.
>>
>>> Anyways, /I think/ variant like this should have been more preferred:
>>>
>>> if (!pm_runtime_enabled(&pdev->dev))
>>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>>> else
>>>          pm_runtime_disable(&pdev->dev);
>>
>> I think it looks to be similar to what is there already.
>>
>> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call
>> if !RPM
>> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
>>         tegra210_i2s_runtime_suspend(&pdev->dev);
> 
> Maybe this is fine for !RPM, but not really fine in a case of enabled
> RPM. Device could be in resumed state after pm_runtime_disable() if it
> wasn't suspended before the disabling.

I don't see any problem with this for the !RPM case.

Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-24  9:07                       ` Jon Hunter
@ 2020-01-24  9:51                         ` Jon Hunter
  2020-01-24 14:04                           ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Jon Hunter @ 2020-01-24  9:51 UTC (permalink / raw)
  To: Dmitry Osipenko, Sameer Pujar
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh


On 24/01/2020 09:07, Jon Hunter wrote:
> 
> On 23/01/2020 15:16, Dmitry Osipenko wrote:
>> 23.01.2020 12:22, Sameer Pujar пишет:
>>>
>>>
>>> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>>>> External email: Use caution opening links or attachments
>>>>
>>>>
>>>> 22.01.2020 14:52, Jon Hunter пишет:
>>>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>>>
>>>>> ...
>>>>>
>>>>>>>>>>>> +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.
>>>>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>>>>> be in
>>>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>>>> Likely
>>>>>>>>> that those few other drivers are wrong.
>>>>>>>>>
>>>>>>>>> [snip]
>>>>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>>>>> would use
>>>>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the
>>>>>>>> new
>>>>>>>> counters?
>>>>>>>> If RPM is not working for some reason, most likely it would be the
>>>>>>>> case
>>>>>>>> for other
>>>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>>>> during
>>>>>>>> removal if
>>>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>>>> still
>>>>>>>> have it,
>>>>>>>> unless there is a real harm in doing so.
>>>>>>> I took a closer look and looks like the counter actually should be
>>>>>>> reset. Still I don't think that it's a good practice to make changes
>>>>>>> underneath of RPM, it may strike back.
>>>>>> If RPM is broken, it probably would have been caught during device
>>>>>> usage.
>>>>>> I will remove explicit suspend here if no any concerns from other
>>>>>> folks.
>>>>>> Thanks.
>>>>> I recall that this was the preferred way of doing this from the RPM
>>>>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>>>>> this as a reference.
>>>>> I believe that this is meant to ensure that the
>>>>> device is always powered-off regardless of it RPM is enabled or not and
>>>>> what the current state is.
>>>> Yes, it was kinda actual for the case of unavailable RPM.
>>>
>>>> Anyways, /I think/ variant like this should have been more preferred:
>>>>
>>>> if (!pm_runtime_enabled(&pdev->dev))
>>>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>>>> else
>>>>          pm_runtime_disable(&pdev->dev);
>>>
>>> I think it looks to be similar to what is there already.
>>>
>>> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call
>>> if !RPM
>>> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
>>>         tegra210_i2s_runtime_suspend(&pdev->dev);
>>
>> Maybe this is fine for !RPM, but not really fine in a case of enabled
>> RPM. Device could be in resumed state after pm_runtime_disable() if it
>> wasn't suspended before the disabling.
> 
> I don't see any problem with this for the !RPM case.

Sorry I meant the RPM case. In other words, I don't see a problem for
neither the RPM case of the !RPM case.

Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-24  9:51                         ` Jon Hunter
@ 2020-01-24 14:04                           ` Dmitry Osipenko
  2020-01-27  5:22                             ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-24 14:04 UTC (permalink / raw)
  To: Jon Hunter, Sameer Pujar
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh

24.01.2020 12:51, Jon Hunter пишет:
> 
> On 24/01/2020 09:07, Jon Hunter wrote:
>>
>> On 23/01/2020 15:16, Dmitry Osipenko wrote:
>>> 23.01.2020 12:22, Sameer Pujar пишет:
>>>>
>>>>
>>>> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>>>>> External email: Use caution opening links or attachments
>>>>>
>>>>>
>>>>> 22.01.2020 14:52, Jon Hunter пишет:
>>>>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>>>>
>>>>>> ...
>>>>>>
>>>>>>>>>>>>> +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.
>>>>>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>>>>>> be in
>>>>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>>>>> Likely
>>>>>>>>>> that those few other drivers are wrong.
>>>>>>>>>>
>>>>>>>>>> [snip]
>>>>>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>>>>>> would use
>>>>>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the
>>>>>>>>> new
>>>>>>>>> counters?
>>>>>>>>> If RPM is not working for some reason, most likely it would be the
>>>>>>>>> case
>>>>>>>>> for other
>>>>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>>>>> during
>>>>>>>>> removal if
>>>>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>>>>> still
>>>>>>>>> have it,
>>>>>>>>> unless there is a real harm in doing so.
>>>>>>>> I took a closer look and looks like the counter actually should be
>>>>>>>> reset. Still I don't think that it's a good practice to make changes
>>>>>>>> underneath of RPM, it may strike back.
>>>>>>> If RPM is broken, it probably would have been caught during device
>>>>>>> usage.
>>>>>>> I will remove explicit suspend here if no any concerns from other
>>>>>>> folks.
>>>>>>> Thanks.
>>>>>> I recall that this was the preferred way of doing this from the RPM
>>>>>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>>>>>> this as a reference.
>>>>>> I believe that this is meant to ensure that the
>>>>>> device is always powered-off regardless of it RPM is enabled or not and
>>>>>> what the current state is.
>>>>> Yes, it was kinda actual for the case of unavailable RPM.
>>>>
>>>>> Anyways, /I think/ variant like this should have been more preferred:
>>>>>
>>>>> if (!pm_runtime_enabled(&pdev->dev))
>>>>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>>>>> else
>>>>>          pm_runtime_disable(&pdev->dev);
>>>>
>>>> I think it looks to be similar to what is there already.
>>>>
>>>> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call
>>>> if !RPM
>>>> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
>>>>         tegra210_i2s_runtime_suspend(&pdev->dev);
>>>
>>> Maybe this is fine for !RPM, but not really fine in a case of enabled
>>> RPM. Device could be in resumed state after pm_runtime_disable() if it
>>> wasn't suspended before the disabling.
>>
>> I don't see any problem with this for the !RPM case.
> 
> Sorry I meant the RPM case. In other words, I don't see a problem for
> neither the RPM case of the !RPM case.

1. Device shall be in RPM-suspended state at the time of driver's
removal, unless there is a bug in the sound driver. Hence why do you
need the dead code which doesn't bring any practical value?

2. Making changes underneath of RPM is simply error-prone. It may hit
badly in the future once something will change in the RPM core.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver
  2020-01-24  4:25       ` Dmitry Osipenko
@ 2020-01-27  5:08         ` Sameer Pujar
  0 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-27  5:08 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, jonathanh, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh



On 1/24/2020 9:55 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 24.01.2020 06:27, Sameer Pujar пишет:
>>
>> On 1/24/2020 6:58 AM, Dmitry Osipenko wrote:
>>> External email: Use caution opening links or attachments
>>>
>>>
>>> 20.01.2020 17:23, Sameer Pujar пишет:
>>> [snip]
>>>> +static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
>>>> +{
>>>> +     struct tegra_admaif *admaif = dev_get_drvdata(dev);
>>>> +     unsigned int ch_stride = TEGRA_ADMAIF_CHANNEL_REG_STRIDE;
>>>> +     unsigned int num_ch = admaif->soc_data->num_ch;
>>>> +     unsigned int rx_base = admaif->soc_data->rx_base;
>>>> +     unsigned int tx_base = admaif->soc_data->tx_base;
>>>> +     unsigned int global_base = admaif->soc_data->global_base;
>>>> +     unsigned int reg_max =
>>>> admaif->soc_data->regmap_conf->max_register;
>>>> +     unsigned int rx_max = rx_base + (num_ch * ch_stride);
>>>> +     unsigned int tx_max = tx_base + (num_ch * ch_stride);
>>>> +
>>>> +     if ((reg >= rx_base) && (reg < rx_max)) {
>>> The braces are not needed around the comparisons because they precede
>>> the AND. Same for all other similar occurrences in the code.
>> While that is true, some prefer to use explicit braces to make it more
>> readable.
>> In the past I was told to use explicitly in such cases.
> At least most of code in kernel (I've seen) doesn't have superfluous
> parens (the curvy thingies actually should be the braces). Readability
> is arguable in this case, I'm finding such code a bit more difficult to
> read, although in some cases parens and spacing may help to read more
> complex constructions.

Yes this is subjective and depends on the individual reading the code. It is
confusing every time, for the sender, about which guideline to follow. 
Resending
the patch/series for only this reason may not be really necessary. Since 
I need
to fix couple of issues in this series I may consider removing the 
explicit braces.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-21 16:04       ` Dmitry Osipenko
@ 2020-01-27  5:11         ` Sameer Pujar
  2020-01-28 22:40           ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-27  5:11 UTC (permalink / raw)
  To: Dmitry Osipenko, perex, tiwai, robh+dt
  Cc: spujar, devicetree, alsa-devel, atalambedu, linux-kernel,
	lgirdwood, jonathanh, viswanathl, sharadg, broonie,
	thierry.reding, linux-tegra, rlokhande, mkumard, dramesh



On 1/21/2020 9:34 PM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 21.01.2020 07:41, Sameer Pujar пишет:
>> On 1/20/2020 9:28 PM, Dmitry Osipenko wrote:
>>> External email: Use caution opening links or attachments
>>>
>>>
>>> Hello Sameer,
>>>
>>> 20.01.2020 17:23, Sameer Pujar пишет:
>>>
>>> [snip]
>>>
>>>> Tegra30 and Tegra124 have an identical CIF programming helper function.
>>> [snip]
>>>
>>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT  24
>>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US        0x3f
>>>> -#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK
>>>> (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US <<
>>>> TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
>>>> -
>>>> -/* Channel count minus 1 */
>>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT   24
>>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7
>>>> -#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK
>>>> (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US <<
>>>> TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
>>> The AUDIOCIF_CTRL bitfields are not the same on T30 and T124, why are
>>> you claiming that programming is identical? Have you actually tried to
>>> test these patches on T30?
>> Oh yes! seems like I overlooked the macro values. Thanks for pointing
>> this. I will retain separate CIF function for Tegra30.
>> I do not have a Tegra30 board with me and hence could not test anything
>> specific to it apart from build sanity.
>> If someone can help me test I would really appreciate.
>>
> I'll help with the testing once all obvious problems will be fixed.

Thanks Dmitry. However I plan to send Tegra30 related patch separately.
In this series I will focus on Tegra210 and later.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-24 14:04                           ` Dmitry Osipenko
@ 2020-01-27  5:22                             ` Sameer Pujar
  2020-01-29  3:41                               ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Sameer Pujar @ 2020-01-27  5:22 UTC (permalink / raw)
  To: Dmitry Osipenko, Jon Hunter
  Cc: spujar, devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh



On 1/24/2020 7:34 PM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 24.01.2020 12:51, Jon Hunter пишет:
>> On 24/01/2020 09:07, Jon Hunter wrote:
>>> On 23/01/2020 15:16, Dmitry Osipenko wrote:
>>>> 23.01.2020 12:22, Sameer Pujar пишет:
>>>>>
>>>>> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>>>>>> External email: Use caution opening links or attachments
>>>>>>
>>>>>>
>>>>>> 22.01.2020 14:52, Jon Hunter пишет:
>>>>>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>>>>>
>>>>>>> ...
>>>>>>>
>>>>>>>>>>>>>> +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.
>>>>>>>>>>> It should matter (if I'm not missing something) because RPM should
>>>>>>>>>>> be in
>>>>>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>>>>>> Likely
>>>>>>>>>>> that those few other drivers are wrong.
>>>>>>>>>>>
>>>>>>>>>>> [snip]
>>>>>>>>>> Once the driver is re-loaded and RPM is enabled, I don't think it
>>>>>>>>>> would use
>>>>>>>>>> the same 'dev' and the corresponding ref count. Doesn't it use the
>>>>>>>>>> new
>>>>>>>>>> counters?
>>>>>>>>>> If RPM is not working for some reason, most likely it would be the
>>>>>>>>>> case
>>>>>>>>>> for other
>>>>>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>>>>>> during
>>>>>>>>>> removal if
>>>>>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>>>>>> still
>>>>>>>>>> have it,
>>>>>>>>>> unless there is a real harm in doing so.
>>>>>>>>> I took a closer look and looks like the counter actually should be
>>>>>>>>> reset. Still I don't think that it's a good practice to make changes
>>>>>>>>> underneath of RPM, it may strike back.
>>>>>>>> If RPM is broken, it probably would have been caught during device
>>>>>>>> usage.
>>>>>>>> I will remove explicit suspend here if no any concerns from other
>>>>>>>> folks.
>>>>>>>> Thanks.
>>>>>>> I recall that this was the preferred way of doing this from the RPM
>>>>>>> folks. Tegra30 I2S driver does the same and Stephen had pointed me to
>>>>>>> this as a reference.
>>>>>>> I believe that this is meant to ensure that the
>>>>>>> device is always powered-off regardless of it RPM is enabled or not and
>>>>>>> what the current state is.
>>>>>> Yes, it was kinda actual for the case of unavailable RPM.
>>>>>> Anyways, /I think/ variant like this should have been more preferred:
>>>>>>
>>>>>> if (!pm_runtime_enabled(&pdev->dev))
>>>>>>           tegra210_i2s_runtime_suspend(&pdev->dev);
>>>>>> else
>>>>>>           pm_runtime_disable(&pdev->dev);
>>>>> I think it looks to be similar to what is there already.
>>>>>
>>>>> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy call
>>>>> if !RPM
>>>>> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always if !RPM
>>>>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>>>> Maybe this is fine for !RPM, but not really fine in a case of enabled
>>>> RPM. Device could be in resumed state after pm_runtime_disable() if it
>>>> wasn't suspended before the disabling.
>>> I don't see any problem with this for the !RPM case.
>> Sorry I meant the RPM case. In other words, I don't see a problem for
>> neither the RPM case of the !RPM case.
> 1. Device shall be in RPM-suspended state at the time of driver's
> removal, unless there is a bug in the sound driver. Hence why do you
> need the dead code which doesn't bring any practical value?
>
> 2. Making changes underneath of RPM is simply error-prone. It may hit
> badly in the future once something will change in the RPM core.

I think we are stretching a bit more here when there is no any real harm.
Right now it works well for both RPM and !RPM case and if we really need to
fix something in future we can fix. Since my initial inclination was keeping
the code as it is and Jon also has similar thoughts, I would retain this 
code.
Sorry Dmitry, we can fix if something comes up and many other drivers would
need this at that time.



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver
  2020-01-24  1:18   ` [alsa-devel] " Dmitry Osipenko
  2020-01-24  3:39     ` Sameer Pujar
@ 2020-01-27  9:45     ` Jon Hunter
  1 sibling, 0 replies; 43+ messages in thread
From: Jon Hunter @ 2020-01-27  9:45 UTC (permalink / raw)
  To: Dmitry Osipenko, Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	viswanathl, sharadg, broonie, thierry.reding, linux-tegra,
	rlokhande, mkumard, dramesh


On 24/01/2020 01:18, Dmitry Osipenko wrote:
> 20.01.2020 17:23, Sameer Pujar пишет:
> [snip]
>> +static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl,
>> +				     struct snd_ctl_elem_value *uctl)
>> +{
>> +	struct snd_soc_component *cmpnt = snd_soc_dapm_kcontrol_component(kctl);
>> +	struct tegra_ahub *ahub = snd_soc_component_get_drvdata(cmpnt);
>> +	struct soc_enum *e = (struct soc_enum *)kctl->private_value;
>> +	unsigned int reg, i, bit_pos = 0;
>> +
>> +	/*
>> +	 * Find the bit position of current MUX input.
>> +	 * If nothing is set, position would be 0 and it corresponds to 'None'.
>> +	 */
>> +	for (i = 0; i < ahub->soc_data->reg_count; i++) {
>> +		unsigned int reg_val;
>> +
>> +		reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
>> +		snd_soc_component_read(cmpnt, reg, &reg_val);
>> +		reg_val &= ahub->soc_data->mask[i];
>> +
>> +		if (reg_val) {
>> +			bit_pos = ffs(reg_val) +
>> +				  (8 * cmpnt->val_bytes * i);
> 
> Multiplication takes precedence, braces are not needed. Same for all
> other occurrences in the code.

Personally, I prefer the above as it is explicit and helps readability.

Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 0/9] add ASoC components for AHUB
  2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
                   ` (8 preceding siblings ...)
  2020-01-20 14:23 ` [PATCH 9/9] arm64: tegra: enable AHUB modules " Sameer Pujar
@ 2020-01-28 10:49 ` Sameer Pujar
  9 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-01-28 10:49 UTC (permalink / raw)
  To: perex, tiwai, robh+dt
  Cc: spujar, broonie, lgirdwood, thierry.reding, jonathanh,
	alsa-devel, devicetree, linux-tegra, linux-kernel, sharadg,
	mkumard, viswanathl, rlokhande, dramesh, atalambedu



On 1/20/2020 7:53 PM, Sameer Pujar wrote:
> Overview
> ========
> The Audio Hub (AHUB) is part of the Audio Processing Engine (APE) which
> comprises a collection of hardware accelerators for audio pre-processing
> and post-processing. It also includes a programmable full crossbar for
> routing audio data across these accelerators.
>
> This series exposes some of these below mentioned HW devices as ASoC
> components for Tegra platforms from Tegra210 onwards.
>   * ADMAIF : The interface between ADMA and AHUB
>   * XBAR   : Crossbar for routing audio samples across various modules
>   * I2S    : Inter-IC Sound Controller
>   * DMIC   : Digital Microphone
>   * DSPK   : Digital Speaker
>
> Following is the summary of current series.
>   1. Add YAML DT binding documentation for above mentioned modules.
>   2. ACIF programming is same for Tegra generations and hence it is moved
>      to a common file.
>   3. Add ASoC driver components for each of the above modules.
>   4. Add DT entries for above components for Tegra210, Tegra186 and
>      Tegra194.
>   5. Enable these components for Jetson-Tx1, Jetson-Tx2 and
>      Jetson-Xavier.
>
> Machine driver series will be sent separately.
>
> Sameer Pujar (9):
>    dt-bindings: sound: tegra: add DT binding for AHUB
>    ASoC: tegra: add support for CIF programming
>    ASoC: tegra: add Tegra210 based DMIC driver
>    ASoC: tegra: add Tegra210 based I2S driver
>    ASoC: tegra: add Tegra210 based AHUB driver
>    ASoC: tegra: add Tegra186 based DSPK driver
>    ASoC: tegra: add Tegra210 based ADMAIF driver
>    arm64: tegra: add AHUB components for few Tegra chips
>    arm64: tegra: enable AHUB modules for few Tegra chips

If any comments on the series, please let me know.
I am planning to publish v2 based on the discussion we had in v1.

Thanks,
Sameer.
>
>   .../bindings/sound/nvidia,tegra186-dspk.yaml       | 105 +++
>   .../bindings/sound/nvidia,tegra210-admaif.yaml     | 165 ++++
>   .../bindings/sound/nvidia,tegra210-ahub.yaml       | 130 +++
>   .../bindings/sound/nvidia,tegra210-dmic.yaml       | 105 +++
>   .../bindings/sound/nvidia,tegra210-i2s.yaml        | 112 +++
>   arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts |  48 ++
>   arch/arm64/boot/dts/nvidia/tegra186.dtsi           | 231 ++++-
>   arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts |  36 +
>   arch/arm64/boot/dts/nvidia/tegra194.dtsi           | 239 +++++-
>   arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts |  40 +
>   arch/arm64/boot/dts/nvidia/tegra210.dtsi           | 145 ++++
>   sound/soc/tegra/Kconfig                            |  56 ++
>   sound/soc/tegra/Makefile                           |  12 +
>   sound/soc/tegra/tegra186_dspk.c                    | 516 +++++++++++
>   sound/soc/tegra/tegra186_dspk.h                    |  73 ++
>   sound/soc/tegra/tegra210_admaif.c                  | 896 ++++++++++++++++++++
>   sound/soc/tegra/tegra210_admaif.h                  | 164 ++++
>   sound/soc/tegra/tegra210_ahub.c                    | 667 +++++++++++++++
>   sound/soc/tegra/tegra210_ahub.h                    | 125 +++
>   sound/soc/tegra/tegra210_dmic.c                    | 522 ++++++++++++
>   sound/soc/tegra/tegra210_dmic.h                    |  85 ++
>   sound/soc/tegra/tegra210_i2s.c                     | 941 +++++++++++++++++++++
>   sound/soc/tegra/tegra210_i2s.h                     | 132 +++
>   sound/soc/tegra/tegra30_ahub.c                     |  94 +-
>   sound/soc/tegra/tegra30_ahub.h                     | 129 ---
>   sound/soc/tegra/tegra30_i2s.c                      |  35 +-
>   sound/soc/tegra/tegra30_i2s.h                      |   7 -
>   sound/soc/tegra/tegra_cif.c                        |  34 +
>   sound/soc/tegra/tegra_cif.h                        |  50 ++
>   sound/soc/tegra/tegra_pcm.c                        | 224 ++++-
>   sound/soc/tegra/tegra_pcm.h                        |  23 +-
>   31 files changed, 5897 insertions(+), 244 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
>   create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
>   create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
>   create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
>   create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
>   create mode 100644 sound/soc/tegra/tegra186_dspk.c
>   create mode 100644 sound/soc/tegra/tegra186_dspk.h
>   create mode 100644 sound/soc/tegra/tegra210_admaif.c
>   create mode 100644 sound/soc/tegra/tegra210_admaif.h
>   create mode 100644 sound/soc/tegra/tegra210_ahub.c
>   create mode 100644 sound/soc/tegra/tegra210_ahub.h
>   create mode 100644 sound/soc/tegra/tegra210_dmic.c
>   create mode 100644 sound/soc/tegra/tegra210_dmic.h
>   create mode 100644 sound/soc/tegra/tegra210_i2s.c
>   create mode 100644 sound/soc/tegra/tegra210_i2s.h
>   create mode 100644 sound/soc/tegra/tegra_cif.c
>   create mode 100644 sound/soc/tegra/tegra_cif.h
>


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 2/9] ASoC: tegra: add support for CIF programming
  2020-01-27  5:11         ` Sameer Pujar
@ 2020-01-28 22:40           ` Dmitry Osipenko
  0 siblings, 0 replies; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-28 22:40 UTC (permalink / raw)
  To: Sameer Pujar, perex, tiwai, robh+dt
  Cc: devicetree, alsa-devel, atalambedu, linux-kernel, lgirdwood,
	jonathanh, viswanathl, sharadg, broonie, thierry.reding,
	linux-tegra, rlokhande, mkumard, dramesh

27.01.2020 08:11, Sameer Pujar пишет:
> Thanks Dmitry. However I plan to send Tegra30 related patch separately.

Feel free to add me to the email's CC if you'll need to test or review
anything related to the T20/30.

> In this series I will focus on Tegra210 and later.

Please be more careful next time and try not to break the existing
functionality, looking forward to v2 :)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-27  5:22                             ` Sameer Pujar
@ 2020-01-29  3:41                               ` Dmitry Osipenko
  2020-02-14 14:05                                 ` Jon Hunter
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-01-29  3:41 UTC (permalink / raw)
  To: Sameer Pujar, Jon Hunter
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh

27.01.2020 08:22, Sameer Pujar пишет:
> 
> 
> On 1/24/2020 7:34 PM, Dmitry Osipenko wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> 24.01.2020 12:51, Jon Hunter пишет:
>>> On 24/01/2020 09:07, Jon Hunter wrote:
>>>> On 23/01/2020 15:16, Dmitry Osipenko wrote:
>>>>> 23.01.2020 12:22, Sameer Pujar пишет:
>>>>>>
>>>>>> On 1/22/2020 9:57 PM, Dmitry Osipenko wrote:
>>>>>>> External email: Use caution opening links or attachments
>>>>>>>
>>>>>>>
>>>>>>> 22.01.2020 14:52, Jon Hunter пишет:
>>>>>>>> On 22/01/2020 07:16, Sameer Pujar wrote:
>>>>>>>>
>>>>>>>> ...
>>>>>>>>
>>>>>>>>>>>>>>> +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.
>>>>>>>>>>>> It should matter (if I'm not missing something) because RPM
>>>>>>>>>>>> should
>>>>>>>>>>>> be in
>>>>>>>>>>>> a wrecked state once you'll try to re-load the driver's module.
>>>>>>>>>>>> Likely
>>>>>>>>>>>> that those few other drivers are wrong.
>>>>>>>>>>>>
>>>>>>>>>>>> [snip]
>>>>>>>>>>> Once the driver is re-loaded and RPM is enabled, I don't
>>>>>>>>>>> think it
>>>>>>>>>>> would use
>>>>>>>>>>> the same 'dev' and the corresponding ref count. Doesn't it
>>>>>>>>>>> use the
>>>>>>>>>>> new
>>>>>>>>>>> counters?
>>>>>>>>>>> If RPM is not working for some reason, most likely it would
>>>>>>>>>>> be the
>>>>>>>>>>> case
>>>>>>>>>>> for other
>>>>>>>>>>> devices. What best driver can do is probably do a force suspend
>>>>>>>>>>> during
>>>>>>>>>>> removal if
>>>>>>>>>>> already not done. I would prefer to keep, since multiple drivers
>>>>>>>>>>> still
>>>>>>>>>>> have it,
>>>>>>>>>>> unless there is a real harm in doing so.
>>>>>>>>>> I took a closer look and looks like the counter actually
>>>>>>>>>> should be
>>>>>>>>>> reset. Still I don't think that it's a good practice to make
>>>>>>>>>> changes
>>>>>>>>>> underneath of RPM, it may strike back.
>>>>>>>>> If RPM is broken, it probably would have been caught during device
>>>>>>>>> usage.
>>>>>>>>> I will remove explicit suspend here if no any concerns from other
>>>>>>>>> folks.
>>>>>>>>> Thanks.
>>>>>>>> I recall that this was the preferred way of doing this from the RPM
>>>>>>>> folks. Tegra30 I2S driver does the same and Stephen had pointed
>>>>>>>> me to
>>>>>>>> this as a reference.
>>>>>>>> I believe that this is meant to ensure that the
>>>>>>>> device is always powered-off regardless of it RPM is enabled or
>>>>>>>> not and
>>>>>>>> what the current state is.
>>>>>>> Yes, it was kinda actual for the case of unavailable RPM.
>>>>>>> Anyways, /I think/ variant like this should have been more
>>>>>>> preferred:
>>>>>>>
>>>>>>> if (!pm_runtime_enabled(&pdev->dev))
>>>>>>>           tegra210_i2s_runtime_suspend(&pdev->dev);
>>>>>>> else
>>>>>>>           pm_runtime_disable(&pdev->dev);
>>>>>> I think it looks to be similar to what is there already.
>>>>>>
>>>>>> pm_runtime_disable(&pdev->dev); // it would turn out to be a dummy
>>>>>> call
>>>>>> if !RPM
>>>>>> if (!pm_runtime_status_suspended(&pdev->dev)) // it is true always
>>>>>> if !RPM
>>>>>>          tegra210_i2s_runtime_suspend(&pdev->dev);
>>>>> Maybe this is fine for !RPM, but not really fine in a case of enabled
>>>>> RPM. Device could be in resumed state after pm_runtime_disable() if it
>>>>> wasn't suspended before the disabling.
>>>> I don't see any problem with this for the !RPM case.
>>> Sorry I meant the RPM case. In other words, I don't see a problem for
>>> neither the RPM case of the !RPM case.
>> 1. Device shall be in RPM-suspended state at the time of driver's
>> removal, unless there is a bug in the sound driver. Hence why do you
>> need the dead code which doesn't bring any practical value?
>>
>> 2. Making changes underneath of RPM is simply error-prone. It may hit
>> badly in the future once something will change in the RPM core.
> 
> I think we are stretching a bit more here when there is no any real harm.
> Right now it works well for both RPM and !RPM case and if we really need to
> fix something in future we can fix. Since my initial inclination was
> keeping
> the code as it is and Jon also has similar thoughts, I would retain this
> code.
> Sorry Dmitry, we can fix if something comes up and many other drivers would
> need this at that time.

The !RPM case isn't supported by Tegra anymore in upstream kernel. I'm
trying to help to make yours driver better and gave you reasons to
remove the unneeded code, while you're keep saying that "there is no
harm to retain it", which is not a reason to clutter up the code. I
don't feel that it's worthwhile to continue arguing here.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-01-29  3:41                               ` Dmitry Osipenko
@ 2020-02-14 14:05                                 ` Jon Hunter
  2020-02-18  1:00                                   ` Dmitry Osipenko
  0 siblings, 1 reply; 43+ messages in thread
From: Jon Hunter @ 2020-02-14 14:05 UTC (permalink / raw)
  To: Dmitry Osipenko, Sameer Pujar
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh


On 29/01/2020 03:41, Dmitry Osipenko wrote:

...

> The !RPM case isn't supported by Tegra anymore in upstream kernel. I'm
> trying to help to make yours driver better and gave you reasons to
> remove the unneeded code, while you're keep saying that "there is no
> harm to retain it", which is not a reason to clutter up the code. I
> don't feel that it's worthwhile to continue arguing here.

On further review, it does look like a reasonable argument to get rid of
this now completely. Like you see less clutter and the soc-core should
ensure that the card is shutdown before unloading. OK, so fine with me.

Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-02-14 14:05                                 ` Jon Hunter
@ 2020-02-18  1:00                                   ` Dmitry Osipenko
  2020-02-19 16:10                                     ` Sameer Pujar
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Osipenko @ 2020-02-18  1:00 UTC (permalink / raw)
  To: Jon Hunter, Sameer Pujar
  Cc: devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh

14.02.2020 17:05, Jon Hunter пишет:
> 
> On 29/01/2020 03:41, Dmitry Osipenko wrote:
> 
> ...
> 
>> The !RPM case isn't supported by Tegra anymore in upstream kernel. I'm
>> trying to help to make yours driver better and gave you reasons to
>> remove the unneeded code, while you're keep saying that "there is no
>> harm to retain it", which is not a reason to clutter up the code. I
>> don't feel that it's worthwhile to continue arguing here.
> 
> On further review, it does look like a reasonable argument to get rid of
> this now completely. Like you see less clutter and the soc-core should
> ensure that the card is shutdown before unloading. OK, so fine with me.

And the RPM core ensures that state is synced on disable, sorry if I
didn't make it clear before.

+1 for less clutter :)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [alsa-devel] [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver
  2020-02-18  1:00                                   ` Dmitry Osipenko
@ 2020-02-19 16:10                                     ` Sameer Pujar
  0 siblings, 0 replies; 43+ messages in thread
From: Sameer Pujar @ 2020-02-19 16:10 UTC (permalink / raw)
  To: Dmitry Osipenko, Jon Hunter
  Cc: spujar, devicetree, alsa-devel, lgirdwood, linux-kernel, broonie,
	atalambedu, tiwai, viswanathl, linux-tegra, robh+dt,
	thierry.reding, sharadg, rlokhande, mkumard, dramesh



On 2/18/2020 6:30 AM, Dmitry Osipenko wrote:
> External email: Use caution opening links or attachments
>
>
> 14.02.2020 17:05, Jon Hunter пишет:
>> On 29/01/2020 03:41, Dmitry Osipenko wrote:
>>
>> ...
>>
>>> The !RPM case isn't supported by Tegra anymore in upstream kernel. I'm
>>> trying to help to make yours driver better and gave you reasons to
>>> remove the unneeded code, while you're keep saying that "there is no
>>> harm to retain it", which is not a reason to clutter up the code. I
>>> don't feel that it's worthwhile to continue arguing here.
>> On further review, it does look like a reasonable argument to get rid of
>> this now completely. Like you see less clutter and the soc-core should
>> ensure that the card is shutdown before unloading. OK, so fine with me.
> And the RPM core ensures that state is synced on disable, sorry if I
> didn't make it clear before.
>
> +1 for less clutter :)

Done, I am removing explicit RPM suspend in the next revision. Thanks 
Dmitry and Jon.


^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2020-02-19 16:10 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-20 14:23 [PATCH 0/9] add ASoC components for AHUB Sameer Pujar
2020-01-20 14:23 ` [PATCH 1/9] dt-bindings: sound: tegra: add DT binding " Sameer Pujar
2020-01-20 14:23 ` [PATCH 2/9] ASoC: tegra: add support for CIF programming Sameer Pujar
2020-01-20 15:58   ` [alsa-devel] " Dmitry Osipenko
2020-01-21  4:41     ` Sameer Pujar
2020-01-21 16:04       ` Dmitry Osipenko
2020-01-27  5:11         ` Sameer Pujar
2020-01-28 22:40           ` Dmitry Osipenko
2020-01-20 14:23 ` [PATCH 3/9] ASoC: tegra: add Tegra210 based DMIC driver Sameer Pujar
2020-01-20 14:23 ` [PATCH 4/9] ASoC: tegra: add Tegra210 based I2S driver Sameer Pujar
2020-01-21  5:15   ` [alsa-devel] " Dmitry Osipenko
2020-01-21 14:21     ` Sameer Pujar
2020-01-21 16:03       ` Dmitry Osipenko
2020-01-22  4:32         ` Sameer Pujar
2020-01-22  6:23           ` Dmitry Osipenko
2020-01-22  7:16             ` Sameer Pujar
2020-01-22 11:52               ` Jon Hunter
2020-01-22 16:27                 ` Dmitry Osipenko
2020-01-23  9:22                   ` Sameer Pujar
2020-01-23 15:16                     ` Dmitry Osipenko
2020-01-24  9:07                       ` Jon Hunter
2020-01-24  9:51                         ` Jon Hunter
2020-01-24 14:04                           ` Dmitry Osipenko
2020-01-27  5:22                             ` Sameer Pujar
2020-01-29  3:41                               ` Dmitry Osipenko
2020-02-14 14:05                                 ` Jon Hunter
2020-02-18  1:00                                   ` Dmitry Osipenko
2020-02-19 16:10                                     ` Sameer Pujar
2020-01-22 16:26               ` Dmitry Osipenko
2020-01-20 14:23 ` [PATCH 5/9] ASoC: tegra: add Tegra210 based AHUB driver Sameer Pujar
2020-01-24  1:18   ` [alsa-devel] " Dmitry Osipenko
2020-01-24  3:39     ` Sameer Pujar
2020-01-24  4:28       ` Dmitry Osipenko
2020-01-27  9:45     ` Jon Hunter
2020-01-20 14:23 ` [PATCH 6/9] ASoC: tegra: add Tegra186 based DSPK driver Sameer Pujar
2020-01-20 14:23 ` [PATCH 7/9] ASoC: tegra: add Tegra210 based ADMAIF driver Sameer Pujar
2020-01-24  1:28   ` [alsa-devel] " Dmitry Osipenko
2020-01-24  3:27     ` Sameer Pujar
2020-01-24  4:25       ` Dmitry Osipenko
2020-01-27  5:08         ` Sameer Pujar
2020-01-20 14:23 ` [PATCH 8/9] arm64: tegra: add AHUB components for few Tegra chips Sameer Pujar
2020-01-20 14:23 ` [PATCH 9/9] arm64: tegra: enable AHUB modules " Sameer Pujar
2020-01-28 10:49 ` [PATCH 0/9] add ASoC components for AHUB Sameer Pujar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).