Merge series "Extend AHUB audio support for Tegra210 and later" from Sameer Pujar <spujar@nvidia.com>:

Earlier as part of series [0], support for ADMAIF and I/O modules (such
as I2S, DMIC and DSPK) was added. This series aims at exposing some of
the AHUB internal modules (listed below), which can be used for audio
pre or post processing.

  * SFC (Sampling Frequency Converter)
  * MVC (Master Volume Control)
  * AMX (Audio Multiplexer)
  * ADX (Audio Demultiplexer)
  * Mixer

These modules can be plugged into audio paths and relevant processing
can be done. The MUX routes are extended to allow add or remove above
modules in the path via mixer controls. This is similar to how specific
ADMAIF channels are connected to relevant I/O module instances at the
moment.

Some of these modules can alter PCM parameters. Consider example of
resampler (44.1 -> 48 kHz) in the path.

  aplay(44.1 kHz) -> ADMAIF -> SFC -> (48 kHz) I2S -> (48kHz) Codec

The modules following SFC should be using converted sample rate and DAIs
need to be configured accordingly. The audio-graph driver provides a
mechanism to fixup the new parameters which can be specified in DT for a
given DAI. Then core uses these new values via fixup callback and then
pass it to respective DAIs hw_param() callback. The "convert-rate",
described in [1], property can be used when there is rate conversion in
the audio path. Similarly "convert-channels" can be used when there is
channel conversion in the path. There is no "convert-xxx" property for
sample size conversions. It can be added if necessary.

[0] https://www.lkml.org/lkml/2020/7/21/1357
[1] Documentation/devicetree/bindings/sound/audio-graph-port.yaml

Changelog
=========

v1 -> v2
--------
 * Put comments for soft reset application in the drivers.
 * Split out mute/volume control logic in put() calls of MVC driver and
   use separate callbacks for the respective kcontrols.
 * Update kcontrol put() callback in MVC driver to return 1 whenever
   there is change. Similar change is done in other drivers too.
 * Use name-prefix.yaml reference for the driver documentation now.
 * Add sound-name-prefix pattern for MIXER driver and use prefix
   accordingly in DT.

Sameer Pujar (13):
  ASoC: soc-pcm: Don't reconnect an already active BE
  ASoC: simple-card-utils: Increase maximum DAI links limit to 512
  ASoC: audio-graph: Fixup CPU endpoint hw_params in a BE<->BE link
  ASoC: dt-bindings: tegra: Few more Tegra210 AHUB modules
  ASoC: tegra: Add routes for few AHUB modules
  ASoC: tegra: Add Tegra210 based MVC driver
  ASoC: tegra: Add Tegra210 based SFC driver
  ASoC: tegra: Add Tegra210 based AMX driver
  ASoC: tegra: Add Tegra210 based ADX driver
  ASoC: tegra: Add Tegra210 based Mixer driver
  arm64: defconfig: Enable few Tegra210 based AHUB drivers
  arm64: tegra: Add few AHUB devices for Tegra210 and later
  arm64: tegra: Extend APE audio support on Jetson platforms

 .../bindings/sound/nvidia,tegra210-adx.yaml        |   76 +
 .../bindings/sound/nvidia,tegra210-ahub.yaml       |   20 +
 .../bindings/sound/nvidia,tegra210-amx.yaml        |   76 +
 .../bindings/sound/nvidia,tegra210-mixer.yaml      |   74 +
 .../bindings/sound/nvidia,tegra210-mvc.yaml        |   76 +
 .../bindings/sound/nvidia,tegra210-sfc.yaml        |   73 +
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 1554 ++++++++-
 arch/arm64/boot/dts/nvidia/tegra186.dtsi           |  120 +
 arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts | 1493 +++++++-
 .../arm64/boot/dts/nvidia/tegra194-p3509-0000.dtsi | 1520 ++++++++-
 arch/arm64/boot/dts/nvidia/tegra194.dtsi           |  116 +
 arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts |  876 +++++
 arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts |  876 +++++
 arch/arm64/boot/dts/nvidia/tegra210.dtsi           |   77 +
 arch/arm64/configs/defconfig                       |    5 +
 include/sound/simple_card_utils.h                  |    2 +-
 sound/soc/generic/audio-graph-card.c               |    4 +-
 sound/soc/soc-pcm.c                                |    4 +
 sound/soc/tegra/Kconfig                            |   48 +
 sound/soc/tegra/Makefile                           |   10 +
 sound/soc/tegra/tegra210_adx.c                     |  531 +++
 sound/soc/tegra/tegra210_adx.h                     |   72 +
 sound/soc/tegra/tegra210_ahub.c                    |  511 ++-
 sound/soc/tegra/tegra210_amx.c                     |  600 ++++
 sound/soc/tegra/tegra210_amx.h                     |   93 +
 sound/soc/tegra/tegra210_mixer.c                   |  674 ++++
 sound/soc/tegra/tegra210_mixer.h                   |  100 +
 sound/soc/tegra/tegra210_mvc.c                     |  645 ++++
 sound/soc/tegra/tegra210_mvc.h                     |  117 +
 sound/soc/tegra/tegra210_sfc.c                     | 3549 ++++++++++++++++++++
 sound/soc/tegra/tegra210_sfc.h                     |   78 +
 31 files changed, 13647 insertions(+), 423 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-adx.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-amx.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-mixer.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-mvc.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra210-sfc.yaml
 create mode 100644 sound/soc/tegra/tegra210_adx.c
 create mode 100644 sound/soc/tegra/tegra210_adx.h
 create mode 100644 sound/soc/tegra/tegra210_amx.c
 create mode 100644 sound/soc/tegra/tegra210_amx.h
 create mode 100644 sound/soc/tegra/tegra210_mixer.c
 create mode 100644 sound/soc/tegra/tegra210_mixer.h
 create mode 100644 sound/soc/tegra/tegra210_mvc.c
 create mode 100644 sound/soc/tegra/tegra210_mvc.h
 create mode 100644 sound/soc/tegra/tegra210_sfc.c
 create mode 100644 sound/soc/tegra/tegra210_sfc.h

--
2.7.4
This commit is contained in:
Mark Brown 2021-09-20 15:46:53 +01:00
commit 2a07ef63f5
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0
22 changed files with 7429 additions and 4 deletions

View file

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nvidia,tegra210-adx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Tegra210 ADX Device Tree Bindings
description: |
The Audio Demultiplexer (ADX) block takes an input stream with up to
16 channels and demultiplexes it into four output streams of up to 16
channels each. A byte RAM helps to form output frames by any combination
of bytes from the input frame. Its design is identical to that of byte
RAM in the AMX except that the data flow direction is reversed.
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Mohan Kumar <mkumard@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
allOf:
- $ref: name-prefix.yaml#
properties:
$nodename:
pattern: "^adx@[0-9a-f]*$"
compatible:
oneOf:
- const: nvidia,tegra210-adx
- items:
- enum:
- nvidia,tegra194-adx
- nvidia,tegra186-adx
- const: nvidia,tegra210-adx
reg:
maxItems: 1
sound-name-prefix:
pattern: "^ADX[1-9]$"
ports:
$ref: /schemas/graph.yaml#/properties/ports
description: |
ADX has one input and four outputs. Accordingly ACIF (Audio Client
Interface) port nodes are defined to represent ADX input (port 0)
and outputs (ports 1 to 4). These are connected to corresponding
ports on AHUB (Audio Hub).
properties:
port@0:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: ADX ACIF input port
patternProperties:
'^port@[1-4]':
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: ADX ACIF output ports
required:
- compatible
- reg
additionalProperties: false
examples:
- |
adx@702d3800 {
compatible = "nvidia,tegra210-adx";
reg = <0x702d3800 0x100>;
sound-name-prefix = "ADX1";
};
...

View file

@ -85,6 +85,26 @@ patternProperties:
type: object
$ref: nvidia,tegra186-dspk.yaml#
'^mvc@[0-9a-f]+$':
type: object
$ref: nvidia,tegra210-mvc.yaml#
'^sfc@[0-9a-f]+$':
type: object
$ref: nvidia,tegra210-sfc.yaml#
'^amx@[0-9a-f]+$':
type: object
$ref: nvidia,tegra210-amx.yaml#
'^adx@[0-9a-f]+$':
type: object
$ref: nvidia,tegra210-adx.yaml#
'^amixer@[0-9a-f]+$':
type: object
$ref: nvidia,tegra210-mixer.yaml#
required:
- compatible
- reg

View file

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nvidia,tegra210-amx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Tegra210 AMX Device Tree Bindings
description: |
The Audio Multiplexer (AMX) block can multiplex up to four input streams
each of which can have maximum 16 channels and generate an output stream
with maximum 16 channels. A byte RAM helps to form an output frame by
any combination of bytes from the input frames.
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Mohan Kumar <mkumard@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
allOf:
- $ref: name-prefix.yaml#
properties:
$nodename:
pattern: "^amx@[0-9a-f]*$"
compatible:
oneOf:
- const: nvidia,tegra210-amx
- items:
- const: nvidia,tegra186-amx
- const: nvidia,tegra210-amx
- const: nvidia,tegra194-amx
reg:
maxItems: 1
sound-name-prefix:
pattern: "^AMX[1-9]$"
ports:
$ref: /schemas/graph.yaml#/properties/ports
description: |
AMX has four inputs and one output. Accordingly ACIF (Audio Client
Interfaces) port nodes are defined to represent AMX inputs (port 0
to 3) and output (port 4). These are connected to corresponding
ports on AHUB (Audio Hub).
patternProperties:
'^port@[0-3]':
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: AMX ACIF input ports
properties:
port@4:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: AMX ACIF output port
required:
- compatible
- reg
additionalProperties: false
examples:
- |
amx@702d3000 {
compatible = "nvidia,tegra210-amx";
reg = <0x702d3000 0x100>;
sound-name-prefix = "AMX1";
};
...

View file

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nvidia,tegra210-mixer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Tegra210 Mixer Device Tree Bindings
description: |
The Mixer supports mixing of up to ten 7.1 audio input streams and
generate five outputs (each of which can be any combination of the
ten input streams).
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Mohan Kumar <mkumard@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
allOf:
- $ref: name-prefix.yaml#
properties:
$nodename:
pattern: "^amixer@[0-9a-f]*$"
compatible:
oneOf:
- const: nvidia,tegra210-amixer
- items:
- enum:
- nvidia,tegra194-amixer
- nvidia,tegra186-amixer
- const: nvidia,tegra210-amixer
reg:
maxItems: 1
sound-name-prefix:
pattern: "^MIXER[1-9]$"
ports:
$ref: /schemas/graph.yaml#/properties/ports
description: |
Mixer has ten inputs and five outputs. Accordingly ACIF (Audio
Client Interfaces) port nodes are defined to represent Mixer
inputs (port 0 to 9) and outputs (port 10 to 14). These are
connected to corresponding ports on AHUB (Audio Hub).
patternProperties:
'^port@[0-9]':
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: Mixer ACIF input ports
'^port@[10-14]':
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: Mixer ACIF output ports
required:
- compatible
- reg
additionalProperties: false
examples:
- |
amixer@702dbb00 {
compatible = "nvidia,tegra210-amixer";
reg = <0x702dbb00 0x800>;
sound-name-prefix = "MIXER1";
};
...

View file

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nvidia,tegra210-mvc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Tegra210 MVC Device Tree Bindings
description: |
The Master Volume Control (MVC) provides gain or attenuation to a digital
signal path. It can be used in input or output signal path for per-stream
volume control or it can be used as master volume control. The MVC block
has one input and one output. The input digital stream can be mono or
multi-channel (up to 7.1 channels) stream. An independent mute control is
also included in the MVC block.
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Mohan Kumar <mkumard@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
allOf:
- $ref: name-prefix.yaml#
properties:
$nodename:
pattern: "^mvc@[0-9a-f]*$"
compatible:
oneOf:
- const: nvidia,tegra210-mvc
- items:
- enum:
- nvidia,tegra194-mvc
- nvidia,tegra186-mvc
- const: nvidia,tegra210-mvc
reg:
maxItems: 1
sound-name-prefix:
pattern: "^MVC[1-9]$"
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: |
MVC ACIF (Audio Client Interface) input port. This is connected
to corresponding ACIF output port on AHUB (Audio Hub).
port@1:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: |
MVC ACIF output port. This is connected to corresponding ACIF
input port on AHUB.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
mvc@702da000 {
compatible = "nvidia,tegra210-mvc";
reg = <0x702da000 0x200>;
sound-name-prefix = "MVC1";
};
...

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nvidia,tegra210-sfc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Tegra210 SFC Device Tree Bindings
description: |
The Sampling Frequency Converter (SFC) converts the sampling frequency
of the input signal from one frequency to another. It supports sampling
frequency conversions of streams of up to two channels (stereo).
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Mohan Kumar <mkumard@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
allOf:
- $ref: name-prefix.yaml#
properties:
$nodename:
pattern: "^sfc@[0-9a-f]*$"
compatible:
oneOf:
- const: nvidia,tegra210-sfc
- items:
- enum:
- nvidia,tegra194-sfc
- nvidia,tegra186-sfc
- const: nvidia,tegra210-sfc
reg:
maxItems: 1
sound-name-prefix:
pattern: "^SFC[1-9]$"
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: |
SFC ACIF (Audio Client Interface) input port. This is connected
to corresponding ACIF output port on AHUB (Audio Hub).
port@1:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
description: |
SFC ACIF output port. This is connected to corresponding ACIF
input port on AHUB.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
sfc@702d2000 {
compatible = "nvidia,tegra210-sfc";
reg = <0x702d2000 0x200>;
sound-name-prefix = "SFC1";
};
...

View file

@ -115,7 +115,7 @@ struct asoc_simple_priv {
((codec) = simple_props_to_dai_codec(props, i)); \
(i)++)
#define SNDRV_MAX_LINKS 128
#define SNDRV_MAX_LINKS 512
struct link_info {
int link; /* number of link */

View file

@ -310,8 +310,10 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
* For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
* there are 'n' BE components in the path.
*/
if (card->component_chaining && !soc_component_is_pcm(cpus))
if (card->component_chaining && !soc_component_is_pcm(cpus)) {
dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
}
asoc_simple_canonicalize_cpu(cpus, is_single_links);
asoc_simple_canonicalize_platform(platforms, cpus);

View file

@ -1395,6 +1395,10 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
if (!fe->dpcm[stream].runtime && !fe->fe_compr)
continue;
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
continue;
/* newly connected FE and BE */
err = dpcm_be_connect(fe, be, stream);
if (err < 0) {

View file

@ -108,6 +108,54 @@ config SND_SOC_TEGRA210_ADMAIF
channel. Buffer size is configurable for each ADMAIIF channel.
Say Y or M if you want to add support for Tegra210 ADMAIF module.
config SND_SOC_TEGRA210_MVC
tristate "Tegra210 MVC module"
help
Config to enable the digital Master Volume Controller (MVC) which
provides gain or attenuation to a digital signal path. It can be
used in input or output signal path. It can be used either for
per-stream volume control or for master volume control.
Say Y or M if you want to add support for Tegra210 MVC module.
config SND_SOC_TEGRA210_SFC
tristate "Tegra210 SFC module"
help
Config to enable the Sampling Frequency Converter (SFC) which
converts the sampling frequency of input signal to another
frequency. It supports sampling frequency conversion of streams
upto 2 channels (stereo).
Say Y or M if you want to add support for Tegra210 SFC module.
config SND_SOC_TEGRA210_AMX
tristate "Tegra210 AMX module"
help
Config to enable the Audio Multiplexer (AMX) which can multiplex
four input streams (each of up to 16 channels) and generate
output stream (of up to 16 channels). A byte RAM helps to form an
output frame by any combination of bytes from the input frames.
Say Y or M if you want to add support for Tegra210 AMX module.
config SND_SOC_TEGRA210_ADX
tristate "Tegra210 ADX module"
help
Config to enable the Audio Demultiplexer (ADX) which takes an
input stream (up to 16 channels) and demultiplexes it into four
output streams (each of up to 16 channels). A byte RAM helps to
form output frames by any combination of bytes from the input
frame. Its design is identical to that of byte RAM in the AMX
except that the data flow direction is reversed.
Say Y or M if you want to add support for Tegra210 ADX module.
config SND_SOC_TEGRA210_MIXER
tristate "Tegra210 Mixer module"
help
Config to enable the Mixer module which can help to mix multiple
audio streams. It supports mixing of upto 10 input streams,
where each stream can contain maximum of 8 channels. It supports
5 output each of which can be a mix of any combination of 10
input streams.
Say Y or M if you want to add support for Tegra210 Mixer module.
config SND_SOC_TEGRA_AUDIO_GRAPH_CARD
tristate "Audio Graph Card based Tegra driver"
depends on SND_AUDIO_GRAPH_CARD

View file

@ -13,6 +13,11 @@ 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
snd-soc-tegra210-mvc-objs := tegra210_mvc.o
snd-soc-tegra210-sfc-objs := tegra210_sfc.o
snd-soc-tegra210-amx-objs := tegra210_amx.o
snd-soc-tegra210-adx-objs := tegra210_adx.o
snd-soc-tegra210-mixer-objs := tegra210_mixer.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
@ -26,6 +31,11 @@ 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
obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o
obj-$(CONFIG_SND_SOC_TEGRA210_SFC) += snd-soc-tegra210-sfc.o
obj-$(CONFIG_SND_SOC_TEGRA210_AMX) += snd-soc-tegra210-amx.o
obj-$(CONFIG_SND_SOC_TEGRA210_ADX) += snd-soc-tegra210-adx.o
obj-$(CONFIG_SND_SOC_TEGRA210_MIXER) += snd-soc-tegra210-mixer.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o

View file

@ -0,0 +1,531 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_adx.c - Tegra210 ADX driver
//
// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra210_adx.h"
#include "tegra_cif.h"
static const struct reg_default tegra210_adx_reg_defaults[] = {
{ TEGRA210_ADX_RX_INT_MASK, 0x00000001},
{ TEGRA210_ADX_RX_CIF_CTRL, 0x00007000},
{ TEGRA210_ADX_TX_INT_MASK, 0x0000000f },
{ TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000},
{ TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000},
{ TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000},
{ TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000},
{ TEGRA210_ADX_CG, 0x1},
{ TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000},
};
static void tegra210_adx_write_map_ram(struct tegra210_adx *adx)
{
int i;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);
for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA,
adx->map[i]);
regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]);
regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]);
}
static int tegra210_adx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
unsigned int val;
int err;
/* Ensure if ADX status is disabled */
err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS,
val, !(val & 0x1), 10, 10000);
if (err < 0) {
dev_err(dai->dev, "failed to stop ADX, err = %d\n", err);
return err;
}
/*
* Soft Reset: Below performs module soft reset which clears
* all FSM logic, flushes flow control of FIFO and resets the
* state register. It also brings module back to disabled
* state (without flushing the data in the pipe).
*/
regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_ADX_SOFT_RESET_SOFT_EN);
err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET,
val, !(val & 0x1), 10, 10000);
if (err < 0) {
dev_err(dai->dev, "failed to reset ADX, err = %d\n", err);
return err;
}
return 0;
}
static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev)
{
struct tegra210_adx *adx = dev_get_drvdata(dev);
regcache_cache_only(adx->regmap, true);
regcache_mark_dirty(adx->regmap);
return 0;
}
static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev)
{
struct tegra210_adx *adx = dev_get_drvdata(dev);
regcache_cache_only(adx->regmap, false);
regcache_sync(adx->regmap);
tegra210_adx_write_map_ram(adx);
return 0;
}
static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
unsigned int channels,
unsigned int format,
unsigned int reg)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
struct tegra_cif_conf cif_conf;
int audio_bits;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
if (channels < 1 || channels > 16)
return -EINVAL;
switch (format) {
case SNDRV_PCM_FORMAT_S8:
audio_bits = TEGRA_ACIF_BITS_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
tegra_set_cif(adx->regmap, reg, &cif_conf);
return 0;
}
static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return tegra210_adx_set_audio_cif(dai, params_channels(params),
params_format(params),
TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));
}
static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return tegra210_adx_set_audio_cif(dai, params_channels(params),
params_format(params),
TEGRA210_ADX_RX_CIF_CTRL);
}
static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
struct soc_mixer_control *mc;
unsigned char *bytes_map = (unsigned char *)&adx->map;
int enabled;
mc = (struct soc_mixer_control *)kcontrol->private_value;
enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32));
if (enabled)
ucontrol->value.integer.value[0] = bytes_map[mc->reg];
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
unsigned char *bytes_map = (unsigned char *)&adx->map;
int value = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;;
if (value >= 0 && value <= 255) {
/* update byte map and enable slot */
bytes_map[mc->reg] = value;
adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32));
} else {
/* reset byte map and disable slot */
bytes_map[mc->reg] = 0;
adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32));
}
return 1;
}
static struct snd_soc_dai_ops tegra210_adx_in_dai_ops = {
.hw_params = tegra210_adx_in_hw_params,
.startup = tegra210_adx_startup,
};
static struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {
.hw_params = tegra210_adx_out_hw_params,
};
#define IN_DAI \
{ \
.name = "ADX-RX-CIF", \
.playback = { \
.stream_name = "RX-CIF-Playback", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "RX-CIF-Capture", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_adx_in_dai_ops, \
}
#define OUT_DAI(id) \
{ \
.name = "ADX-TX" #id "-CIF", \
.playback = { \
.stream_name = "TX" #id "-CIF-Playback",\
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "TX" #id "-CIF-Capture", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_adx_out_dai_ops, \
}
static struct snd_soc_dai_driver tegra210_adx_dais[] = {
IN_DAI,
OUT_DAI(1),
OUT_DAI(2),
OUT_DAI(3),
OUT_DAI(4),
};
static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE,
TEGRA210_ADX_ENABLE_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),
};
#define STREAM_ROUTES(id, sname) \
{ "XBAR-" sname, NULL, "XBAR-TX" }, \
{ "RX-CIF-" sname, NULL, "XBAR-" sname }, \
{ "RX", NULL, "RX-CIF-" sname }, \
{ "TX" #id, NULL, "RX" }, \
{ "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
{ "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
{ "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
#define ADX_ROUTES(id) \
STREAM_ROUTES(id, "Playback"), \
STREAM_ROUTES(id, "Capture")
#define STREAM_ROUTES(id, sname) \
{ "XBAR-" sname, NULL, "XBAR-TX" }, \
{ "RX-CIF-" sname, NULL, "XBAR-" sname }, \
{ "RX", NULL, "RX-CIF-" sname }, \
{ "TX" #id, NULL, "RX" }, \
{ "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
{ "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
{ "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
#define ADX_ROUTES(id) \
STREAM_ROUTES(id, "Playback"), \
STREAM_ROUTES(id, "Capture")
static const struct snd_soc_dapm_route tegra210_adx_routes[] = {
ADX_ROUTES(1),
ADX_ROUTES(2),
ADX_ROUTES(3),
ADX_ROUTES(4),
};
#define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \
SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
tegra210_adx_get_byte_map, \
tegra210_adx_put_byte_map)
static struct snd_kcontrol_new tegra210_adx_controls[] = {
TEGRA210_ADX_BYTE_MAP_CTRL(0),
TEGRA210_ADX_BYTE_MAP_CTRL(1),
TEGRA210_ADX_BYTE_MAP_CTRL(2),
TEGRA210_ADX_BYTE_MAP_CTRL(3),
TEGRA210_ADX_BYTE_MAP_CTRL(4),
TEGRA210_ADX_BYTE_MAP_CTRL(5),
TEGRA210_ADX_BYTE_MAP_CTRL(6),
TEGRA210_ADX_BYTE_MAP_CTRL(7),
TEGRA210_ADX_BYTE_MAP_CTRL(8),
TEGRA210_ADX_BYTE_MAP_CTRL(9),
TEGRA210_ADX_BYTE_MAP_CTRL(10),
TEGRA210_ADX_BYTE_MAP_CTRL(11),
TEGRA210_ADX_BYTE_MAP_CTRL(12),
TEGRA210_ADX_BYTE_MAP_CTRL(13),
TEGRA210_ADX_BYTE_MAP_CTRL(14),
TEGRA210_ADX_BYTE_MAP_CTRL(15),
TEGRA210_ADX_BYTE_MAP_CTRL(16),
TEGRA210_ADX_BYTE_MAP_CTRL(17),
TEGRA210_ADX_BYTE_MAP_CTRL(18),
TEGRA210_ADX_BYTE_MAP_CTRL(19),
TEGRA210_ADX_BYTE_MAP_CTRL(20),
TEGRA210_ADX_BYTE_MAP_CTRL(21),
TEGRA210_ADX_BYTE_MAP_CTRL(22),
TEGRA210_ADX_BYTE_MAP_CTRL(23),
TEGRA210_ADX_BYTE_MAP_CTRL(24),
TEGRA210_ADX_BYTE_MAP_CTRL(25),
TEGRA210_ADX_BYTE_MAP_CTRL(26),
TEGRA210_ADX_BYTE_MAP_CTRL(27),
TEGRA210_ADX_BYTE_MAP_CTRL(28),
TEGRA210_ADX_BYTE_MAP_CTRL(29),
TEGRA210_ADX_BYTE_MAP_CTRL(30),
TEGRA210_ADX_BYTE_MAP_CTRL(31),
TEGRA210_ADX_BYTE_MAP_CTRL(32),
TEGRA210_ADX_BYTE_MAP_CTRL(33),
TEGRA210_ADX_BYTE_MAP_CTRL(34),
TEGRA210_ADX_BYTE_MAP_CTRL(35),
TEGRA210_ADX_BYTE_MAP_CTRL(36),
TEGRA210_ADX_BYTE_MAP_CTRL(37),
TEGRA210_ADX_BYTE_MAP_CTRL(38),
TEGRA210_ADX_BYTE_MAP_CTRL(39),
TEGRA210_ADX_BYTE_MAP_CTRL(40),
TEGRA210_ADX_BYTE_MAP_CTRL(41),
TEGRA210_ADX_BYTE_MAP_CTRL(42),
TEGRA210_ADX_BYTE_MAP_CTRL(43),
TEGRA210_ADX_BYTE_MAP_CTRL(44),
TEGRA210_ADX_BYTE_MAP_CTRL(45),
TEGRA210_ADX_BYTE_MAP_CTRL(46),
TEGRA210_ADX_BYTE_MAP_CTRL(47),
TEGRA210_ADX_BYTE_MAP_CTRL(48),
TEGRA210_ADX_BYTE_MAP_CTRL(49),
TEGRA210_ADX_BYTE_MAP_CTRL(50),
TEGRA210_ADX_BYTE_MAP_CTRL(51),
TEGRA210_ADX_BYTE_MAP_CTRL(52),
TEGRA210_ADX_BYTE_MAP_CTRL(53),
TEGRA210_ADX_BYTE_MAP_CTRL(54),
TEGRA210_ADX_BYTE_MAP_CTRL(55),
TEGRA210_ADX_BYTE_MAP_CTRL(56),
TEGRA210_ADX_BYTE_MAP_CTRL(57),
TEGRA210_ADX_BYTE_MAP_CTRL(58),
TEGRA210_ADX_BYTE_MAP_CTRL(59),
TEGRA210_ADX_BYTE_MAP_CTRL(60),
TEGRA210_ADX_BYTE_MAP_CTRL(61),
TEGRA210_ADX_BYTE_MAP_CTRL(62),
TEGRA210_ADX_BYTE_MAP_CTRL(63),
};
static const struct snd_soc_component_driver tegra210_adx_cmpnt = {
.dapm_widgets = tegra210_adx_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets),
.dapm_routes = tegra210_adx_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes),
.controls = tegra210_adx_controls,
.num_controls = ARRAY_SIZE(tegra210_adx_controls),
};
static bool tegra210_adx_wr_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL:
case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL:
case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG:
case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1:
case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_adx_rd_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_adx_volatile_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_ADX_RX_STATUS:
case TEGRA210_ADX_RX_INT_STATUS:
case TEGRA210_ADX_RX_INT_SET:
case TEGRA210_ADX_TX_STATUS:
case TEGRA210_ADX_TX_INT_STATUS:
case TEGRA210_ADX_TX_INT_SET:
case TEGRA210_ADX_SOFT_RESET:
case TEGRA210_ADX_STATUS:
case TEGRA210_ADX_INT_STATUS:
case TEGRA210_ADX_CFG_RAM_CTRL:
case TEGRA210_ADX_CFG_RAM_DATA:
return true;
default:
break;
}
return false;
}
static const struct regmap_config tegra210_adx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_ADX_CFG_RAM_DATA,
.writeable_reg = tegra210_adx_wr_reg,
.readable_reg = tegra210_adx_rd_reg,
.volatile_reg = tegra210_adx_volatile_reg,
.reg_defaults = tegra210_adx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra210_adx_of_match[] = {
{ .compatible = "nvidia,tegra210-adx" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
static int tegra210_adx_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_adx *adx;
void __iomem *regs;
int err;
adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL);
if (!adx)
return -ENOMEM;
dev_set_drvdata(dev, adx);
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
adx->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_adx_regmap_config);
if (IS_ERR(adx->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(adx->regmap);
}
regcache_cache_only(adx->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt,
tegra210_adx_dais,
ARRAY_SIZE(tegra210_adx_dais));
if (err) {
dev_err(dev, "can't register ADX component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_adx_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_adx_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend,
tegra210_adx_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_adx_driver = {
.driver = {
.name = "tegra210-adx",
.of_match_table = tegra210_adx_of_match,
.pm = &tegra210_adx_pm_ops,
},
.probe = tegra210_adx_platform_probe,
.remove = tegra210_adx_platform_remove,
};
module_platform_driver(tegra210_adx_driver);
MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_adx.h - Definitions for Tegra210 ADX driver
*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_ADX_H__
#define __TEGRA210_ADX_H__
/* Register offsets from TEGRA210_ADX*_BASE */
#define TEGRA210_ADX_RX_STATUS 0x0c
#define TEGRA210_ADX_RX_INT_STATUS 0x10
#define TEGRA210_ADX_RX_INT_MASK 0x14
#define TEGRA210_ADX_RX_INT_SET 0x18
#define TEGRA210_ADX_RX_INT_CLEAR 0x1c
#define TEGRA210_ADX_RX_CIF_CTRL 0x20
#define TEGRA210_ADX_TX_STATUS 0x4c
#define TEGRA210_ADX_TX_INT_STATUS 0x50
#define TEGRA210_ADX_TX_INT_MASK 0x54
#define TEGRA210_ADX_TX_INT_SET 0x58
#define TEGRA210_ADX_TX_INT_CLEAR 0x5c
#define TEGRA210_ADX_TX1_CIF_CTRL 0x60
#define TEGRA210_ADX_TX2_CIF_CTRL 0x64
#define TEGRA210_ADX_TX3_CIF_CTRL 0x68
#define TEGRA210_ADX_TX4_CIF_CTRL 0x6c
#define TEGRA210_ADX_ENABLE 0x80
#define TEGRA210_ADX_SOFT_RESET 0x84
#define TEGRA210_ADX_CG 0x88
#define TEGRA210_ADX_STATUS 0x8c
#define TEGRA210_ADX_INT_STATUS 0x90
#define TEGRA210_ADX_CTRL 0xa4
#define TEGRA210_ADX_IN_BYTE_EN0 0xa8
#define TEGRA210_ADX_IN_BYTE_EN1 0xac
#define TEGRA210_ADX_CFG_RAM_CTRL 0xb8
#define TEGRA210_ADX_CFG_RAM_DATA 0xbc
/* Fields in TEGRA210_ADX_ENABLE */
#define TEGRA210_ADX_ENABLE_SHIFT 0
/* Fields in TEGRA210_ADX_CFG_RAM_CTRL */
#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0
#define TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT 14
#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
/* Fields in TEGRA210_ADX_SOFT_RESET */
#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT 0
#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
#define TEGRA210_ADX_SOFT_RESET_SOFT_EN (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
#define TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4
#define TEGRA210_ADX_RAM_DEPTH 16
#define TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT 6
#define TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT 2
#define TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT 0
struct tegra210_adx {
struct regmap *regmap;
unsigned int map[TEGRA210_ADX_RAM_DEPTH];
unsigned int byte_mask[2];
};
#endif

View file

@ -105,14 +105,68 @@ static struct snd_soc_dai_driver tegra210_ahub_dais[] = {
DAI(ADMAIF8),
DAI(ADMAIF9),
DAI(ADMAIF10),
/* XBAR <-> I2S <-> Codec */
DAI(I2S1),
DAI(I2S2),
DAI(I2S3),
DAI(I2S4),
DAI(I2S5),
/* XBAR <- DMIC <- Codec */
DAI(DMIC1),
DAI(DMIC2),
DAI(DMIC3),
/* XBAR -> SFC -> XBAR */
DAI(SFC1 RX),
DAI(SFC1 TX),
DAI(SFC2 RX),
DAI(SFC2 TX),
DAI(SFC3 RX),
DAI(SFC3 TX),
DAI(SFC4 RX),
DAI(SFC4 TX),
/* XBAR -> MVC -> XBAR */
DAI(MVC1 RX),
DAI(MVC1 TX),
DAI(MVC2 RX),
DAI(MVC2 TX),
/* XBAR -> AMX(4:1) -> XBAR */
DAI(AMX1 RX1),
DAI(AMX1 RX2),
DAI(AMX1 RX3),
DAI(AMX1 RX4),
DAI(AMX1),
DAI(AMX2 RX1),
DAI(AMX2 RX2),
DAI(AMX2 RX3),
DAI(AMX2 RX4),
DAI(AMX2),
/* XBAR -> ADX(1:4) -> XBAR */
DAI(ADX1),
DAI(ADX1 TX1),
DAI(ADX1 TX2),
DAI(ADX1 TX3),
DAI(ADX1 TX4),
DAI(ADX2),
DAI(ADX2 TX1),
DAI(ADX2 TX2),
DAI(ADX2 TX3),
DAI(ADX2 TX4),
/* XBAR -> MIXER(10:5) -> XBAR */
DAI(MIXER1 RX1),
DAI(MIXER1 RX2),
DAI(MIXER1 RX3),
DAI(MIXER1 RX4),
DAI(MIXER1 RX5),
DAI(MIXER1 RX6),
DAI(MIXER1 RX7),
DAI(MIXER1 RX8),
DAI(MIXER1 RX9),
DAI(MIXER1 RX10),
DAI(MIXER1 TX1),
DAI(MIXER1 TX2),
DAI(MIXER1 TX3),
DAI(MIXER1 TX4),
DAI(MIXER1 TX5),
};
static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
@ -136,18 +190,93 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
DAI(ADMAIF18),
DAI(ADMAIF19),
DAI(ADMAIF20),
/* XBAR <-> I2S <-> Codec */
DAI(I2S1),
DAI(I2S2),
DAI(I2S3),
DAI(I2S4),
DAI(I2S5),
DAI(I2S6),
/* XBAR <- DMIC <- Codec */
DAI(DMIC1),
DAI(DMIC2),
DAI(DMIC3),
DAI(DMIC4),
/* XBAR -> DSPK -> Codec */
DAI(DSPK1),
DAI(DSPK2),
/* XBAR -> SFC -> XBAR */
DAI(SFC1 RX),
DAI(SFC1 TX),
DAI(SFC2 RX),
DAI(SFC2 TX),
DAI(SFC3 RX),
DAI(SFC3 TX),
DAI(SFC4 RX),
DAI(SFC4 TX),
/* XBAR -> MVC -> XBAR */
DAI(MVC1 RX),
DAI(MVC1 TX),
DAI(MVC2 RX),
DAI(MVC2 TX),
/* XBAR -> AMX(4:1) -> XBAR */
DAI(AMX1 RX1),
DAI(AMX1 RX2),
DAI(AMX1 RX3),
DAI(AMX1 RX4),
DAI(AMX1),
DAI(AMX2 RX1),
DAI(AMX2 RX2),
DAI(AMX2 RX3),
DAI(AMX2 RX4),
DAI(AMX2),
DAI(AMX3 RX1),
DAI(AMX3 RX2),
DAI(AMX3 RX3),
DAI(AMX3 RX4),
DAI(AMX3),
DAI(AMX4 RX1),
DAI(AMX4 RX2),
DAI(AMX4 RX3),
DAI(AMX4 RX4),
DAI(AMX4),
/* XBAR -> ADX(1:4) -> XBAR */
DAI(ADX1),
DAI(ADX1 TX1),
DAI(ADX1 TX2),
DAI(ADX1 TX3),
DAI(ADX1 TX4),
DAI(ADX2),
DAI(ADX2 TX1),
DAI(ADX2 TX2),
DAI(ADX2 TX3),
DAI(ADX2 TX4),
DAI(ADX3),
DAI(ADX3 TX1),
DAI(ADX3 TX2),
DAI(ADX3 TX3),
DAI(ADX3 TX4),
DAI(ADX4),
DAI(ADX4 TX1),
DAI(ADX4 TX2),
DAI(ADX4 TX3),
DAI(ADX4 TX4),
/* XBAR -> MIXER1(10:5) -> XBAR */
DAI(MIXER1 RX1),
DAI(MIXER1 RX2),
DAI(MIXER1 RX3),
DAI(MIXER1 RX4),
DAI(MIXER1 RX5),
DAI(MIXER1 RX6),
DAI(MIXER1 RX7),
DAI(MIXER1 RX8),
DAI(MIXER1 RX9),
DAI(MIXER1 RX10),
DAI(MIXER1 TX1),
DAI(MIXER1 TX2),
DAI(MIXER1 TX3),
DAI(MIXER1 TX4),
DAI(MIXER1 TX5),
};
static const char * const tegra210_ahub_mux_texts[] = {
@ -170,6 +299,27 @@ static const char * const tegra210_ahub_mux_texts[] = {
"DMIC1",
"DMIC2",
"DMIC3",
"SFC1",
"SFC2",
"SFC3",
"SFC4",
"MVC1",
"MVC2",
"AMX1",
"AMX2",
"ADX1 TX1",
"ADX1 TX2",
"ADX1 TX3",
"ADX1 TX4",
"ADX2 TX1",
"ADX2 TX2",
"ADX2 TX3",
"ADX2 TX4",
"MIXER1 TX1",
"MIXER1 TX2",
"MIXER1 TX3",
"MIXER1 TX4",
"MIXER1 TX5",
};
static const char * const tegra186_ahub_mux_texts[] = {
@ -204,10 +354,42 @@ static const char * const tegra186_ahub_mux_texts[] = {
"DMIC2",
"DMIC3",
"DMIC4",
"SFC1",
"SFC2",
"SFC3",
"SFC4",
"MVC1",
"MVC2",
"AMX1",
"AMX2",
"AMX3",
"AMX4",
"ADX1 TX1",
"ADX1 TX2",
"ADX1 TX3",
"ADX1 TX4",
"ADX2 TX1",
"ADX2 TX2",
"ADX2 TX3",
"ADX2 TX4",
"ADX3 TX1",
"ADX3 TX2",
"ADX3 TX3",
"ADX3 TX4",
"ADX4 TX1",
"ADX4 TX2",
"ADX4 TX3",
"ADX4 TX4",
"MIXER1 TX1",
"MIXER1 TX2",
"MIXER1 TX3",
"MIXER1 TX4",
"MIXER1 TX5",
};
static const unsigned int tegra210_ahub_mux_values[] = {
0,
/* ADMAIF */
MUX_VALUE(0, 0),
MUX_VALUE(0, 1),
MUX_VALUE(0, 2),
@ -218,18 +400,47 @@ static const unsigned int tegra210_ahub_mux_values[] = {
MUX_VALUE(0, 7),
MUX_VALUE(0, 8),
MUX_VALUE(0, 9),
/* I2S */
MUX_VALUE(0, 16),
MUX_VALUE(0, 17),
MUX_VALUE(0, 18),
MUX_VALUE(0, 19),
MUX_VALUE(0, 20),
/* DMIC */
MUX_VALUE(2, 18),
MUX_VALUE(2, 19),
MUX_VALUE(2, 20),
/* SFC */
MUX_VALUE(0, 24),
MUX_VALUE(0, 25),
MUX_VALUE(0, 26),
MUX_VALUE(0, 27),
/* MVC */
MUX_VALUE(2, 8),
MUX_VALUE(2, 9),
/* AMX */
MUX_VALUE(1, 8),
MUX_VALUE(1, 9),
/* ADX */
MUX_VALUE(2, 24),
MUX_VALUE(2, 25),
MUX_VALUE(2, 26),
MUX_VALUE(2, 27),
MUX_VALUE(2, 28),
MUX_VALUE(2, 29),
MUX_VALUE(2, 30),
MUX_VALUE(2, 31),
/* MIXER */
MUX_VALUE(1, 0),
MUX_VALUE(1, 1),
MUX_VALUE(1, 2),
MUX_VALUE(1, 3),
MUX_VALUE(1, 4),
};
static const unsigned int tegra186_ahub_mux_values[] = {
0,
/* ADMAIF */
MUX_VALUE(0, 0),
MUX_VALUE(0, 1),
MUX_VALUE(0, 2),
@ -246,20 +457,59 @@ static const unsigned int tegra186_ahub_mux_values[] = {
MUX_VALUE(0, 13),
MUX_VALUE(0, 14),
MUX_VALUE(0, 15),
/* I2S */
MUX_VALUE(0, 16),
MUX_VALUE(0, 17),
MUX_VALUE(0, 18),
MUX_VALUE(0, 19),
MUX_VALUE(0, 20),
MUX_VALUE(0, 21),
/* ADMAIF */
MUX_VALUE(3, 16),
MUX_VALUE(3, 17),
MUX_VALUE(3, 18),
MUX_VALUE(3, 19),
/* DMIC */
MUX_VALUE(2, 18),
MUX_VALUE(2, 19),
MUX_VALUE(2, 20),
MUX_VALUE(2, 21),
/* SFC */
MUX_VALUE(0, 24),
MUX_VALUE(0, 25),
MUX_VALUE(0, 26),
MUX_VALUE(0, 27),
/* MVC */
MUX_VALUE(2, 8),
MUX_VALUE(2, 9),
/* AMX */
MUX_VALUE(1, 8),
MUX_VALUE(1, 9),
MUX_VALUE(1, 10),
MUX_VALUE(1, 11),
/* ADX */
MUX_VALUE(2, 24),
MUX_VALUE(2, 25),
MUX_VALUE(2, 26),
MUX_VALUE(2, 27),
MUX_VALUE(2, 28),
MUX_VALUE(2, 29),
MUX_VALUE(2, 30),
MUX_VALUE(2, 31),
MUX_VALUE(3, 0),
MUX_VALUE(3, 1),
MUX_VALUE(3, 2),
MUX_VALUE(3, 3),
MUX_VALUE(3, 4),
MUX_VALUE(3, 5),
MUX_VALUE(3, 6),
MUX_VALUE(3, 7),
/* MIXER */
MUX_VALUE(1, 0),
MUX_VALUE(1, 1),
MUX_VALUE(1, 2),
MUX_VALUE(1, 3),
MUX_VALUE(1, 4),
};
/* Controls for t210 */
@ -278,6 +528,32 @@ MUX_ENUM_CTRL_DECL(t210_i2s2_tx, 0x11);
MUX_ENUM_CTRL_DECL(t210_i2s3_tx, 0x12);
MUX_ENUM_CTRL_DECL(t210_i2s4_tx, 0x13);
MUX_ENUM_CTRL_DECL(t210_i2s5_tx, 0x14);
MUX_ENUM_CTRL_DECL(t210_sfc1_tx, 0x18);
MUX_ENUM_CTRL_DECL(t210_sfc2_tx, 0x19);
MUX_ENUM_CTRL_DECL(t210_sfc3_tx, 0x1a);
MUX_ENUM_CTRL_DECL(t210_sfc4_tx, 0x1b);
MUX_ENUM_CTRL_DECL(t210_mvc1_tx, 0x48);
MUX_ENUM_CTRL_DECL(t210_mvc2_tx, 0x49);
MUX_ENUM_CTRL_DECL(t210_amx11_tx, 0x50);
MUX_ENUM_CTRL_DECL(t210_amx12_tx, 0x51);
MUX_ENUM_CTRL_DECL(t210_amx13_tx, 0x52);
MUX_ENUM_CTRL_DECL(t210_amx14_tx, 0x53);
MUX_ENUM_CTRL_DECL(t210_amx21_tx, 0x54);
MUX_ENUM_CTRL_DECL(t210_amx22_tx, 0x55);
MUX_ENUM_CTRL_DECL(t210_amx23_tx, 0x56);
MUX_ENUM_CTRL_DECL(t210_amx24_tx, 0x57);
MUX_ENUM_CTRL_DECL(t210_adx1_tx, 0x58);
MUX_ENUM_CTRL_DECL(t210_adx2_tx, 0x59);
MUX_ENUM_CTRL_DECL(t210_mixer11_tx, 0x20);
MUX_ENUM_CTRL_DECL(t210_mixer12_tx, 0x21);
MUX_ENUM_CTRL_DECL(t210_mixer13_tx, 0x22);
MUX_ENUM_CTRL_DECL(t210_mixer14_tx, 0x23);
MUX_ENUM_CTRL_DECL(t210_mixer15_tx, 0x24);
MUX_ENUM_CTRL_DECL(t210_mixer16_tx, 0x25);
MUX_ENUM_CTRL_DECL(t210_mixer17_tx, 0x26);
MUX_ENUM_CTRL_DECL(t210_mixer18_tx, 0x27);
MUX_ENUM_CTRL_DECL(t210_mixer19_tx, 0x28);
MUX_ENUM_CTRL_DECL(t210_mixer110_tx, 0x29);
/* Controls for t186 */
MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00);
@ -308,6 +584,42 @@ MUX_ENUM_CTRL_DECL_186(t186_admaif17_tx, 0x68);
MUX_ENUM_CTRL_DECL_186(t186_admaif18_tx, 0x69);
MUX_ENUM_CTRL_DECL_186(t186_admaif19_tx, 0x6a);
MUX_ENUM_CTRL_DECL_186(t186_admaif20_tx, 0x6b);
MUX_ENUM_CTRL_DECL_186(t186_sfc1_tx, 0x18);
MUX_ENUM_CTRL_DECL_186(t186_sfc2_tx, 0x19);
MUX_ENUM_CTRL_DECL_186(t186_sfc3_tx, 0x1a);
MUX_ENUM_CTRL_DECL_186(t186_sfc4_tx, 0x1b);
MUX_ENUM_CTRL_DECL_186(t186_mvc1_tx, 0x48);
MUX_ENUM_CTRL_DECL_186(t186_mvc2_tx, 0x49);
MUX_ENUM_CTRL_DECL_186(t186_amx11_tx, 0x50);
MUX_ENUM_CTRL_DECL_186(t186_amx12_tx, 0x51);
MUX_ENUM_CTRL_DECL_186(t186_amx13_tx, 0x52);
MUX_ENUM_CTRL_DECL_186(t186_amx14_tx, 0x53);
MUX_ENUM_CTRL_DECL_186(t186_amx21_tx, 0x54);
MUX_ENUM_CTRL_DECL_186(t186_amx22_tx, 0x55);
MUX_ENUM_CTRL_DECL_186(t186_amx23_tx, 0x56);
MUX_ENUM_CTRL_DECL_186(t186_amx24_tx, 0x57);
MUX_ENUM_CTRL_DECL_186(t186_amx31_tx, 0x58);
MUX_ENUM_CTRL_DECL_186(t186_amx32_tx, 0x59);
MUX_ENUM_CTRL_DECL_186(t186_amx33_tx, 0x5a);
MUX_ENUM_CTRL_DECL_186(t186_amx34_tx, 0x5b);
MUX_ENUM_CTRL_DECL_186(t186_amx41_tx, 0x64);
MUX_ENUM_CTRL_DECL_186(t186_amx42_tx, 0x65);
MUX_ENUM_CTRL_DECL_186(t186_amx43_tx, 0x66);
MUX_ENUM_CTRL_DECL_186(t186_amx44_tx, 0x67);
MUX_ENUM_CTRL_DECL_186(t186_adx1_tx, 0x60);
MUX_ENUM_CTRL_DECL_186(t186_adx2_tx, 0x61);
MUX_ENUM_CTRL_DECL_186(t186_adx3_tx, 0x62);
MUX_ENUM_CTRL_DECL_186(t186_adx4_tx, 0x63);
MUX_ENUM_CTRL_DECL_186(t186_mixer11_tx, 0x20);
MUX_ENUM_CTRL_DECL_186(t186_mixer12_tx, 0x21);
MUX_ENUM_CTRL_DECL_186(t186_mixer13_tx, 0x22);
MUX_ENUM_CTRL_DECL_186(t186_mixer14_tx, 0x23);
MUX_ENUM_CTRL_DECL_186(t186_mixer15_tx, 0x24);
MUX_ENUM_CTRL_DECL_186(t186_mixer16_tx, 0x25);
MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26);
MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27);
MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28);
MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29);
/*
* The number of entries in, and order of, this array is closely tied to the
@ -333,6 +645,47 @@ static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = {
TX_WIDGETS("DMIC1"),
TX_WIDGETS("DMIC2"),
TX_WIDGETS("DMIC3"),
WIDGETS("SFC1", t210_sfc1_tx),
WIDGETS("SFC2", t210_sfc2_tx),
WIDGETS("SFC3", t210_sfc3_tx),
WIDGETS("SFC4", t210_sfc4_tx),
WIDGETS("MVC1", t210_mvc1_tx),
WIDGETS("MVC2", t210_mvc2_tx),
WIDGETS("AMX1 RX1", t210_amx11_tx),
WIDGETS("AMX1 RX2", t210_amx12_tx),
WIDGETS("AMX1 RX3", t210_amx13_tx),
WIDGETS("AMX1 RX4", t210_amx14_tx),
WIDGETS("AMX2 RX1", t210_amx21_tx),
WIDGETS("AMX2 RX2", t210_amx22_tx),
WIDGETS("AMX2 RX3", t210_amx23_tx),
WIDGETS("AMX2 RX4", t210_amx24_tx),
TX_WIDGETS("AMX1"),
TX_WIDGETS("AMX2"),
WIDGETS("ADX1", t210_adx1_tx),
WIDGETS("ADX2", t210_adx2_tx),
TX_WIDGETS("ADX1 TX1"),
TX_WIDGETS("ADX1 TX2"),
TX_WIDGETS("ADX1 TX3"),
TX_WIDGETS("ADX1 TX4"),
TX_WIDGETS("ADX2 TX1"),
TX_WIDGETS("ADX2 TX2"),
TX_WIDGETS("ADX2 TX3"),
TX_WIDGETS("ADX2 TX4"),
WIDGETS("MIXER1 RX1", t210_mixer11_tx),
WIDGETS("MIXER1 RX2", t210_mixer12_tx),
WIDGETS("MIXER1 RX3", t210_mixer13_tx),
WIDGETS("MIXER1 RX4", t210_mixer14_tx),
WIDGETS("MIXER1 RX5", t210_mixer15_tx),
WIDGETS("MIXER1 RX6", t210_mixer16_tx),
WIDGETS("MIXER1 RX7", t210_mixer17_tx),
WIDGETS("MIXER1 RX8", t210_mixer18_tx),
WIDGETS("MIXER1 RX9", t210_mixer19_tx),
WIDGETS("MIXER1 RX10", t210_mixer110_tx),
TX_WIDGETS("MIXER1 TX1"),
TX_WIDGETS("MIXER1 TX2"),
TX_WIDGETS("MIXER1 TX3"),
TX_WIDGETS("MIXER1 TX4"),
TX_WIDGETS("MIXER1 TX5"),
};
static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
@ -368,6 +721,67 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
TX_WIDGETS("DMIC4"),
WIDGETS("DSPK1", t186_dspk1_tx),
WIDGETS("DSPK2", t186_dspk2_tx),
WIDGETS("SFC1", t186_sfc1_tx),
WIDGETS("SFC2", t186_sfc2_tx),
WIDGETS("SFC3", t186_sfc3_tx),
WIDGETS("SFC4", t186_sfc4_tx),
WIDGETS("MVC1", t186_mvc1_tx),
WIDGETS("MVC2", t186_mvc2_tx),
WIDGETS("AMX1 RX1", t186_amx11_tx),
WIDGETS("AMX1 RX2", t186_amx12_tx),
WIDGETS("AMX1 RX3", t186_amx13_tx),
WIDGETS("AMX1 RX4", t186_amx14_tx),
WIDGETS("AMX2 RX1", t186_amx21_tx),
WIDGETS("AMX2 RX2", t186_amx22_tx),
WIDGETS("AMX2 RX3", t186_amx23_tx),
WIDGETS("AMX2 RX4", t186_amx24_tx),
WIDGETS("AMX3 RX1", t186_amx31_tx),
WIDGETS("AMX3 RX2", t186_amx32_tx),
WIDGETS("AMX3 RX3", t186_amx33_tx),
WIDGETS("AMX3 RX4", t186_amx34_tx),
WIDGETS("AMX4 RX1", t186_amx41_tx),
WIDGETS("AMX4 RX2", t186_amx42_tx),
WIDGETS("AMX4 RX3", t186_amx43_tx),
WIDGETS("AMX4 RX4", t186_amx44_tx),
TX_WIDGETS("AMX1"),
TX_WIDGETS("AMX2"),
TX_WIDGETS("AMX3"),
TX_WIDGETS("AMX4"),
WIDGETS("ADX1", t186_adx1_tx),
WIDGETS("ADX2", t186_adx2_tx),
WIDGETS("ADX3", t186_adx3_tx),
WIDGETS("ADX4", t186_adx4_tx),
TX_WIDGETS("ADX1 TX1"),
TX_WIDGETS("ADX1 TX2"),
TX_WIDGETS("ADX1 TX3"),
TX_WIDGETS("ADX1 TX4"),
TX_WIDGETS("ADX2 TX1"),
TX_WIDGETS("ADX2 TX2"),
TX_WIDGETS("ADX2 TX3"),
TX_WIDGETS("ADX2 TX4"),
TX_WIDGETS("ADX3 TX1"),
TX_WIDGETS("ADX3 TX2"),
TX_WIDGETS("ADX3 TX3"),
TX_WIDGETS("ADX3 TX4"),
TX_WIDGETS("ADX4 TX1"),
TX_WIDGETS("ADX4 TX2"),
TX_WIDGETS("ADX4 TX3"),
TX_WIDGETS("ADX4 TX4"),
WIDGETS("MIXER1 RX1", t186_mixer11_tx),
WIDGETS("MIXER1 RX2", t186_mixer12_tx),
WIDGETS("MIXER1 RX3", t186_mixer13_tx),
WIDGETS("MIXER1 RX4", t186_mixer14_tx),
WIDGETS("MIXER1 RX5", t186_mixer15_tx),
WIDGETS("MIXER1 RX6", t186_mixer16_tx),
WIDGETS("MIXER1 RX7", t186_mixer17_tx),
WIDGETS("MIXER1 RX8", t186_mixer18_tx),
WIDGETS("MIXER1 RX9", t186_mixer19_tx),
WIDGETS("MIXER1 RX10", t186_mixer110_tx),
TX_WIDGETS("MIXER1 TX1"),
TX_WIDGETS("MIXER1 TX2"),
TX_WIDGETS("MIXER1 TX3"),
TX_WIDGETS("MIXER1 TX4"),
TX_WIDGETS("MIXER1 TX5"),
};
#define TEGRA_COMMON_MUX_ROUTES(name) \
@ -389,7 +803,28 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
{ name " Mux", "I2S5", "I2S5 XBAR-RX" }, \
{ name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \
{ name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \
{ name " Mux", "DMIC3", "DMIC3 XBAR-RX" },
{ name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \
{ name " Mux", "SFC1", "SFC1 XBAR-RX" }, \
{ name " Mux", "SFC2", "SFC2 XBAR-RX" }, \
{ name " Mux", "SFC3", "SFC3 XBAR-RX" }, \
{ name " Mux", "SFC4", "SFC4 XBAR-RX" }, \
{ name " Mux", "MVC1", "MVC1 XBAR-RX" }, \
{ name " Mux", "MVC2", "MVC2 XBAR-RX" }, \
{ name " Mux", "AMX1", "AMX1 XBAR-RX" }, \
{ name " Mux", "AMX2", "AMX2 XBAR-RX" }, \
{ name " Mux", "ADX1 TX1", "ADX1 TX1 XBAR-RX" }, \
{ name " Mux", "ADX1 TX2", "ADX1 TX2 XBAR-RX" }, \
{ name " Mux", "ADX1 TX3", "ADX1 TX3 XBAR-RX" }, \
{ name " Mux", "ADX1 TX4", "ADX1 TX4 XBAR-RX" }, \
{ name " Mux", "ADX2 TX1", "ADX2 TX1 XBAR-RX" }, \
{ name " Mux", "ADX2 TX2", "ADX2 TX2 XBAR-RX" }, \
{ name " Mux", "ADX2 TX3", "ADX2 TX3 XBAR-RX" }, \
{ name " Mux", "ADX2 TX4", "ADX2 TX4 XBAR-RX" }, \
{ name " Mux", "MIXER1 TX1", "MIXER1 TX1 XBAR-RX" }, \
{ name " Mux", "MIXER1 TX2", "MIXER1 TX2 XBAR-RX" }, \
{ name " Mux", "MIXER1 TX3", "MIXER1 TX3 XBAR-RX" }, \
{ name " Mux", "MIXER1 TX4", "MIXER1 TX4 XBAR-RX" }, \
{ name " Mux", "MIXER1 TX5", "MIXER1 TX5 XBAR-RX" },
#define TEGRA186_ONLY_MUX_ROUTES(name) \
{ name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \
@ -403,7 +838,17 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = {
{ name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \
{ name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \
{ name " Mux", "I2S6", "I2S6 XBAR-RX" }, \
{ name " Mux", "DMIC4", "DMIC4 XBAR-RX" },
{ name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \
{ name " Mux", "AMX3", "AMX3 XBAR-RX" }, \
{ name " Mux", "AMX4", "AMX4 XBAR-RX" }, \
{ name " Mux", "ADX3 TX1", "ADX3 TX1 XBAR-RX" }, \
{ name " Mux", "ADX3 TX2", "ADX3 TX2 XBAR-RX" }, \
{ name " Mux", "ADX3 TX3", "ADX3 TX3 XBAR-RX" }, \
{ name " Mux", "ADX3 TX4", "ADX3 TX4 XBAR-RX" }, \
{ name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \
{ name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \
{ name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \
{ name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" },
#define TEGRA210_MUX_ROUTES(name) \
TEGRA_COMMON_MUX_ROUTES(name)
@ -450,6 +895,32 @@ static const struct snd_soc_dapm_route tegra210_ahub_routes[] = {
TEGRA210_MUX_ROUTES("I2S3")
TEGRA210_MUX_ROUTES("I2S4")
TEGRA210_MUX_ROUTES("I2S5")
TEGRA210_MUX_ROUTES("SFC1")
TEGRA210_MUX_ROUTES("SFC2")
TEGRA210_MUX_ROUTES("SFC3")
TEGRA210_MUX_ROUTES("SFC4")
TEGRA210_MUX_ROUTES("MVC1")
TEGRA210_MUX_ROUTES("MVC2")
TEGRA210_MUX_ROUTES("AMX1 RX1")
TEGRA210_MUX_ROUTES("AMX1 RX2")
TEGRA210_MUX_ROUTES("AMX1 RX3")
TEGRA210_MUX_ROUTES("AMX1 RX4")
TEGRA210_MUX_ROUTES("AMX2 RX1")
TEGRA210_MUX_ROUTES("AMX2 RX2")
TEGRA210_MUX_ROUTES("AMX2 RX3")
TEGRA210_MUX_ROUTES("AMX2 RX4")
TEGRA210_MUX_ROUTES("ADX1")
TEGRA210_MUX_ROUTES("ADX2")
TEGRA210_MUX_ROUTES("MIXER1 RX1")
TEGRA210_MUX_ROUTES("MIXER1 RX2")
TEGRA210_MUX_ROUTES("MIXER1 RX3")
TEGRA210_MUX_ROUTES("MIXER1 RX4")
TEGRA210_MUX_ROUTES("MIXER1 RX5")
TEGRA210_MUX_ROUTES("MIXER1 RX6")
TEGRA210_MUX_ROUTES("MIXER1 RX7")
TEGRA210_MUX_ROUTES("MIXER1 RX8")
TEGRA210_MUX_ROUTES("MIXER1 RX9")
TEGRA210_MUX_ROUTES("MIXER1 RX10")
};
static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
@ -501,6 +972,42 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
TEGRA186_MUX_ROUTES("I2S6")
TEGRA186_MUX_ROUTES("DSPK1")
TEGRA186_MUX_ROUTES("DSPK2")
TEGRA186_MUX_ROUTES("SFC1")
TEGRA186_MUX_ROUTES("SFC2")
TEGRA186_MUX_ROUTES("SFC3")
TEGRA186_MUX_ROUTES("SFC4")
TEGRA186_MUX_ROUTES("MVC1")
TEGRA186_MUX_ROUTES("MVC2")
TEGRA186_MUX_ROUTES("AMX1 RX1")
TEGRA186_MUX_ROUTES("AMX1 RX2")
TEGRA186_MUX_ROUTES("AMX1 RX3")
TEGRA186_MUX_ROUTES("AMX1 RX4")
TEGRA186_MUX_ROUTES("AMX2 RX1")
TEGRA186_MUX_ROUTES("AMX2 RX2")
TEGRA186_MUX_ROUTES("AMX2 RX3")
TEGRA186_MUX_ROUTES("AMX2 RX4")
TEGRA186_MUX_ROUTES("AMX3 RX1")
TEGRA186_MUX_ROUTES("AMX3 RX2")
TEGRA186_MUX_ROUTES("AMX3 RX3")
TEGRA186_MUX_ROUTES("AMX3 RX4")
TEGRA186_MUX_ROUTES("AMX4 RX1")
TEGRA186_MUX_ROUTES("AMX4 RX2")
TEGRA186_MUX_ROUTES("AMX4 RX3")
TEGRA186_MUX_ROUTES("AMX4 RX4")
TEGRA186_MUX_ROUTES("ADX1")
TEGRA186_MUX_ROUTES("ADX2")
TEGRA186_MUX_ROUTES("ADX3")
TEGRA186_MUX_ROUTES("ADX4")
TEGRA186_MUX_ROUTES("MIXER1 RX1")
TEGRA186_MUX_ROUTES("MIXER1 RX2")
TEGRA186_MUX_ROUTES("MIXER1 RX3")
TEGRA186_MUX_ROUTES("MIXER1 RX4")
TEGRA186_MUX_ROUTES("MIXER1 RX5")
TEGRA186_MUX_ROUTES("MIXER1 RX6")
TEGRA186_MUX_ROUTES("MIXER1 RX7")
TEGRA186_MUX_ROUTES("MIXER1 RX8")
TEGRA186_MUX_ROUTES("MIXER1 RX9")
TEGRA186_MUX_ROUTES("MIXER1 RX10")
};
static const struct snd_soc_component_driver tegra210_ahub_component = {

View file

@ -0,0 +1,600 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_amx.c - Tegra210 AMX driver
//
// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra210_amx.h"
#include "tegra_cif.h"
/*
* The counter is in terms of AHUB clock cycles. If a frame is not
* received within these clock cycles, the AMX input channel gets
* automatically disabled. For now the counter is calculated as a
* function of sample rate (8 kHz) and AHUB clock (49.152 MHz).
* If later an accurate number is needed, the counter needs to be
* calculated at runtime.
*
* count = ahub_clk / sample_rate
*/
#define TEGRA194_MAX_FRAME_IDLE_COUNT 0x1800
#define AMX_CH_REG(id, reg) ((reg) + ((id) * TEGRA210_AMX_AUDIOCIF_CH_STRIDE))
static const struct reg_default tegra210_amx_reg_defaults[] = {
{ TEGRA210_AMX_RX_INT_MASK, 0x0000000f},
{ TEGRA210_AMX_RX1_CIF_CTRL, 0x00007000},
{ TEGRA210_AMX_RX2_CIF_CTRL, 0x00007000},
{ TEGRA210_AMX_RX3_CIF_CTRL, 0x00007000},
{ TEGRA210_AMX_RX4_CIF_CTRL, 0x00007000},
{ TEGRA210_AMX_TX_INT_MASK, 0x00000001},
{ TEGRA210_AMX_TX_CIF_CTRL, 0x00007000},
{ TEGRA210_AMX_CG, 0x1},
{ TEGRA210_AMX_CFG_RAM_CTRL, 0x00004000},
};
static void tegra210_amx_write_map_ram(struct tegra210_amx *amx)
{
int i;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL,
TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE);
for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++)
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA,
amx->map[i]);
regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN0, amx->byte_mask[0]);
regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN1, amx->byte_mask[1]);
}
static int tegra210_amx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
unsigned int val;
int err;
/* Ensure if AMX is disabled */
err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_STATUS, val,
!(val & 0x1), 10, 10000);
if (err < 0) {
dev_err(dai->dev, "failed to stop AMX, err = %d\n", err);
return err;
}
/*
* Soft Reset: Below performs module soft reset which clears
* all FSM logic, flushes flow control of FIFO and resets the
* state register. It also brings module back to disabled
* state (without flushing the data in the pipe).
*/
regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_AMX_SOFT_RESET_SOFT_EN);
err = regmap_read_poll_timeout(amx->regmap, TEGRA210_AMX_SOFT_RESET,
val, !(val & 0x1), 10, 10000);
if (err < 0) {
dev_err(dai->dev, "failed to reset AMX, err = %d\n", err);
return err;
}
return 0;
}
static int __maybe_unused tegra210_amx_runtime_suspend(struct device *dev)
{
struct tegra210_amx *amx = dev_get_drvdata(dev);
regcache_cache_only(amx->regmap, true);
regcache_mark_dirty(amx->regmap);
return 0;
}
static int __maybe_unused tegra210_amx_runtime_resume(struct device *dev)
{
struct tegra210_amx *amx = dev_get_drvdata(dev);
regcache_cache_only(amx->regmap, false);
regcache_sync(amx->regmap);
regmap_update_bits(amx->regmap,
TEGRA210_AMX_CTRL,
TEGRA210_AMX_CTRL_RX_DEP_MASK,
TEGRA210_AMX_WAIT_ON_ANY << TEGRA210_AMX_CTRL_RX_DEP_SHIFT);
tegra210_amx_write_map_ram(amx);
return 0;
}
static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params,
unsigned int reg)
{
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
audio_bits = TEGRA_ACIF_BITS_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
tegra_set_cif(amx->regmap, reg, &cif_conf);
return 0;
}
static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
if (amx->soc_data->auto_disable) {
regmap_write(amx->regmap,
AMX_CH_REG(dai->id, TEGRA194_AMX_RX1_FRAME_PERIOD),
TEGRA194_MAX_FRAME_IDLE_COUNT);
regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1);
}
return tegra210_amx_set_audio_cif(dai, params,
AMX_CH_REG(dai->id, TEGRA210_AMX_RX1_CIF_CTRL));
}
static int tegra210_amx_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return tegra210_amx_set_audio_cif(dai, params,
TEGRA210_AMX_TX_CIF_CTRL);
}
static int tegra210_amx_get_byte_map(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
unsigned char *bytes_map = (unsigned char *)&amx->map;
int reg = mc->reg;
int enabled;
if (reg > 31)
enabled = amx->byte_mask[1] & (1 << (reg - 32));
else
enabled = amx->byte_mask[0] & (1 << reg);
if (enabled)
ucontrol->value.integer.value[0] = bytes_map[reg];
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int tegra210_amx_put_byte_map(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
unsigned char *bytes_map = (unsigned char *)&amx->map;
int reg = mc->reg;
int value = ucontrol->value.integer.value[0];
if (value >= 0 && value <= 255) {
/* Update byte map and enable slot */
bytes_map[reg] = value;
if (reg > 31)
amx->byte_mask[1] |= (1 << (reg - 32));
else
amx->byte_mask[0] |= (1 << reg);
} else {
/* Reset byte map and disable slot */
bytes_map[reg] = 0;
if (reg > 31)
amx->byte_mask[1] &= ~(1 << (reg - 32));
else
amx->byte_mask[0] &= ~(1 << reg);
}
return 1;
}
static struct snd_soc_dai_ops tegra210_amx_out_dai_ops = {
.hw_params = tegra210_amx_out_hw_params,
.startup = tegra210_amx_startup,
};
static struct snd_soc_dai_ops tegra210_amx_in_dai_ops = {
.hw_params = tegra210_amx_in_hw_params,
};
#define IN_DAI(id) \
{ \
.name = "AMX-RX-CIF" #id, \
.playback = { \
.stream_name = "RX" #id "-CIF-Playback",\
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "RX" #id "-CIF-Capture", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_amx_in_dai_ops, \
}
#define OUT_DAI \
{ \
.name = "AMX-TX-CIF", \
.playback = { \
.stream_name = "TX-CIF-Playback", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "TX-CIF-Capture", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_amx_out_dai_ops, \
}
static struct snd_soc_dai_driver tegra210_amx_dais[] = {
IN_DAI(1),
IN_DAI(2),
IN_DAI(3),
IN_DAI(4),
OUT_DAI,
};
static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = {
SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0),
SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0),
SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0),
SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_AMX_ENABLE,
TEGRA210_AMX_ENABLE_SHIFT, 0),
};
#define STREAM_ROUTES(id, sname) \
{ "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \
{ "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname },\
{ "RX" #id, NULL, "RX" #id "-CIF-" sname }, \
{ "TX", NULL, "RX" #id }, \
{ "TX-CIF-" sname, NULL, "TX" }, \
{ "XBAR-" sname, NULL, "TX-CIF-" sname }, \
{ "XBAR-RX", NULL, "XBAR-" sname }
#define AMX_ROUTES(id) \
STREAM_ROUTES(id, "Playback"), \
STREAM_ROUTES(id, "Capture")
static const struct snd_soc_dapm_route tegra210_amx_routes[] = {
AMX_ROUTES(1),
AMX_ROUTES(2),
AMX_ROUTES(3),
AMX_ROUTES(4),
};
#define TEGRA210_AMX_BYTE_MAP_CTRL(reg) \
SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
tegra210_amx_get_byte_map, \
tegra210_amx_put_byte_map)
static struct snd_kcontrol_new tegra210_amx_controls[] = {
TEGRA210_AMX_BYTE_MAP_CTRL(0),
TEGRA210_AMX_BYTE_MAP_CTRL(1),
TEGRA210_AMX_BYTE_MAP_CTRL(2),
TEGRA210_AMX_BYTE_MAP_CTRL(3),
TEGRA210_AMX_BYTE_MAP_CTRL(4),
TEGRA210_AMX_BYTE_MAP_CTRL(5),
TEGRA210_AMX_BYTE_MAP_CTRL(6),
TEGRA210_AMX_BYTE_MAP_CTRL(7),
TEGRA210_AMX_BYTE_MAP_CTRL(8),
TEGRA210_AMX_BYTE_MAP_CTRL(9),
TEGRA210_AMX_BYTE_MAP_CTRL(10),
TEGRA210_AMX_BYTE_MAP_CTRL(11),
TEGRA210_AMX_BYTE_MAP_CTRL(12),
TEGRA210_AMX_BYTE_MAP_CTRL(13),
TEGRA210_AMX_BYTE_MAP_CTRL(14),
TEGRA210_AMX_BYTE_MAP_CTRL(15),
TEGRA210_AMX_BYTE_MAP_CTRL(16),
TEGRA210_AMX_BYTE_MAP_CTRL(17),
TEGRA210_AMX_BYTE_MAP_CTRL(18),
TEGRA210_AMX_BYTE_MAP_CTRL(19),
TEGRA210_AMX_BYTE_MAP_CTRL(20),
TEGRA210_AMX_BYTE_MAP_CTRL(21),
TEGRA210_AMX_BYTE_MAP_CTRL(22),
TEGRA210_AMX_BYTE_MAP_CTRL(23),
TEGRA210_AMX_BYTE_MAP_CTRL(24),
TEGRA210_AMX_BYTE_MAP_CTRL(25),
TEGRA210_AMX_BYTE_MAP_CTRL(26),
TEGRA210_AMX_BYTE_MAP_CTRL(27),
TEGRA210_AMX_BYTE_MAP_CTRL(28),
TEGRA210_AMX_BYTE_MAP_CTRL(29),
TEGRA210_AMX_BYTE_MAP_CTRL(30),
TEGRA210_AMX_BYTE_MAP_CTRL(31),
TEGRA210_AMX_BYTE_MAP_CTRL(32),
TEGRA210_AMX_BYTE_MAP_CTRL(33),
TEGRA210_AMX_BYTE_MAP_CTRL(34),
TEGRA210_AMX_BYTE_MAP_CTRL(35),
TEGRA210_AMX_BYTE_MAP_CTRL(36),
TEGRA210_AMX_BYTE_MAP_CTRL(37),
TEGRA210_AMX_BYTE_MAP_CTRL(38),
TEGRA210_AMX_BYTE_MAP_CTRL(39),
TEGRA210_AMX_BYTE_MAP_CTRL(40),
TEGRA210_AMX_BYTE_MAP_CTRL(41),
TEGRA210_AMX_BYTE_MAP_CTRL(42),
TEGRA210_AMX_BYTE_MAP_CTRL(43),
TEGRA210_AMX_BYTE_MAP_CTRL(44),
TEGRA210_AMX_BYTE_MAP_CTRL(45),
TEGRA210_AMX_BYTE_MAP_CTRL(46),
TEGRA210_AMX_BYTE_MAP_CTRL(47),
TEGRA210_AMX_BYTE_MAP_CTRL(48),
TEGRA210_AMX_BYTE_MAP_CTRL(49),
TEGRA210_AMX_BYTE_MAP_CTRL(50),
TEGRA210_AMX_BYTE_MAP_CTRL(51),
TEGRA210_AMX_BYTE_MAP_CTRL(52),
TEGRA210_AMX_BYTE_MAP_CTRL(53),
TEGRA210_AMX_BYTE_MAP_CTRL(54),
TEGRA210_AMX_BYTE_MAP_CTRL(55),
TEGRA210_AMX_BYTE_MAP_CTRL(56),
TEGRA210_AMX_BYTE_MAP_CTRL(57),
TEGRA210_AMX_BYTE_MAP_CTRL(58),
TEGRA210_AMX_BYTE_MAP_CTRL(59),
TEGRA210_AMX_BYTE_MAP_CTRL(60),
TEGRA210_AMX_BYTE_MAP_CTRL(61),
TEGRA210_AMX_BYTE_MAP_CTRL(62),
TEGRA210_AMX_BYTE_MAP_CTRL(63),
};
static const struct snd_soc_component_driver tegra210_amx_cmpnt = {
.dapm_widgets = tegra210_amx_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets),
.dapm_routes = tegra210_amx_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_amx_routes),
.controls = tegra210_amx_controls,
.num_controls = ARRAY_SIZE(tegra210_amx_controls),
};
static bool tegra210_amx_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_AMX_RX_INT_MASK ... TEGRA210_AMX_RX4_CIF_CTRL:
case TEGRA210_AMX_TX_INT_MASK ... TEGRA210_AMX_CG:
case TEGRA210_AMX_CTRL ... TEGRA210_AMX_CYA:
case TEGRA210_AMX_CFG_RAM_CTRL ... TEGRA210_AMX_CFG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra194_amx_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD:
return true;
default:
return tegra210_amx_wr_reg(dev, reg);
}
}
static bool tegra210_amx_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_AMX_RX_STATUS ... TEGRA210_AMX_CFG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra194_amx_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA194_AMX_RX1_FRAME_PERIOD ... TEGRA194_AMX_RX4_FRAME_PERIOD:
return true;
default:
return tegra210_amx_rd_reg(dev, reg);
}
}
static bool tegra210_amx_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_AMX_RX_STATUS:
case TEGRA210_AMX_RX_INT_STATUS:
case TEGRA210_AMX_RX_INT_SET:
case TEGRA210_AMX_TX_STATUS:
case TEGRA210_AMX_TX_INT_STATUS:
case TEGRA210_AMX_TX_INT_SET:
case TEGRA210_AMX_SOFT_RESET:
case TEGRA210_AMX_STATUS:
case TEGRA210_AMX_INT_STATUS:
case TEGRA210_AMX_CFG_RAM_CTRL:
case TEGRA210_AMX_CFG_RAM_DATA:
return true;
default:
break;
}
return false;
}
static const struct regmap_config tegra210_amx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_AMX_CFG_RAM_DATA,
.writeable_reg = tegra210_amx_wr_reg,
.readable_reg = tegra210_amx_rd_reg,
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct regmap_config tegra194_amx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA194_AMX_RX4_LAST_FRAME_PERIOD,
.writeable_reg = tegra194_amx_wr_reg,
.readable_reg = tegra194_amx_rd_reg,
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct tegra210_amx_soc_data soc_data_tegra210 = {
.regmap_conf = &tegra210_amx_regmap_config,
};
static const struct tegra210_amx_soc_data soc_data_tegra194 = {
.regmap_conf = &tegra194_amx_regmap_config,
.auto_disable = true,
};
static const struct of_device_id tegra210_amx_of_match[] = {
{ .compatible = "nvidia,tegra210-amx", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra194-amx", .data = &soc_data_tegra194 },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_amx_of_match);
static int tegra210_amx_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_amx *amx;
void __iomem *regs;
int err;
const struct of_device_id *match;
struct tegra210_amx_soc_data *soc_data;
match = of_match_device(tegra210_amx_of_match, dev);
soc_data = (struct tegra210_amx_soc_data *)match->data;
amx = devm_kzalloc(dev, sizeof(*amx), GFP_KERNEL);
if (!amx)
return -ENOMEM;
amx->soc_data = soc_data;
dev_set_drvdata(dev, amx);
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
amx->regmap = devm_regmap_init_mmio(dev, regs,
soc_data->regmap_conf);
if (IS_ERR(amx->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(amx->regmap);
}
regcache_cache_only(amx->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_amx_cmpnt,
tegra210_amx_dais,
ARRAY_SIZE(tegra210_amx_dais));
if (err) {
dev_err(dev, "can't register AMX component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_amx_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_amx_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_amx_runtime_suspend,
tegra210_amx_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_amx_driver = {
.driver = {
.name = "tegra210-amx",
.of_match_table = tegra210_amx_of_match,
.pm = &tegra210_amx_pm_ops,
},
.probe = tegra210_amx_platform_probe,
.remove = tegra210_amx_platform_remove,
};
module_platform_driver(tegra210_amx_driver);
MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 AMX ASoC driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,93 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_amx.h - Definitions for Tegra210 AMX driver
*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_AMX_H__
#define __TEGRA210_AMX_H__
/* Register offsets from TEGRA210_AMX*_BASE */
#define TEGRA210_AMX_RX_STATUS 0x0c
#define TEGRA210_AMX_RX_INT_STATUS 0x10
#define TEGRA210_AMX_RX_INT_MASK 0x14
#define TEGRA210_AMX_RX_INT_SET 0x18
#define TEGRA210_AMX_RX_INT_CLEAR 0x1c
#define TEGRA210_AMX_RX1_CIF_CTRL 0x20
#define TEGRA210_AMX_RX2_CIF_CTRL 0x24
#define TEGRA210_AMX_RX3_CIF_CTRL 0x28
#define TEGRA210_AMX_RX4_CIF_CTRL 0x2c
#define TEGRA210_AMX_TX_STATUS 0x4c
#define TEGRA210_AMX_TX_INT_STATUS 0x50
#define TEGRA210_AMX_TX_INT_MASK 0x54
#define TEGRA210_AMX_TX_INT_SET 0x58
#define TEGRA210_AMX_TX_INT_CLEAR 0x5c
#define TEGRA210_AMX_TX_CIF_CTRL 0x60
#define TEGRA210_AMX_ENABLE 0x80
#define TEGRA210_AMX_SOFT_RESET 0x84
#define TEGRA210_AMX_CG 0x88
#define TEGRA210_AMX_STATUS 0x8c
#define TEGRA210_AMX_INT_STATUS 0x90
#define TEGRA210_AMX_CTRL 0xa4
#define TEGRA210_AMX_OUT_BYTE_EN0 0xa8
#define TEGRA210_AMX_OUT_BYTE_EN1 0xac
#define TEGRA210_AMX_CYA 0xb0
#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8
#define TEGRA210_AMX_CFG_RAM_DATA 0xbc
#define TEGRA194_AMX_RX1_FRAME_PERIOD 0xc0
#define TEGRA194_AMX_RX4_FRAME_PERIOD 0xcc
#define TEGRA194_AMX_RX4_LAST_FRAME_PERIOD 0xdc
/* Fields in TEGRA210_AMX_ENABLE */
#define TEGRA210_AMX_ENABLE_SHIFT 0
/* Fields in TEGRA210_AMX_CTRL */
#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT 14
#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK (3 << TEGRA210_AMX_CTRL_MSTR_RX_NUM_SHIFT)
#define TEGRA210_AMX_CTRL_RX_DEP_SHIFT 12
#define TEGRA210_AMX_CTRL_RX_DEP_MASK (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT)
/* Fields in TEGRA210_AMX_CFG_RAM_CTRL */
#define TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT 14
#define TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_AMX_CFG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
#define TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT 0
/* Fields in TEGRA210_AMX_SOFT_RESET */
#define TEGRA210_AMX_SOFT_RESET_SOFT_EN 1
#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK TEGRA210_AMX_SOFT_RESET_SOFT_EN
#define TEGRA210_AMX_AUDIOCIF_CH_STRIDE 4
#define TEGRA210_AMX_RAM_DEPTH 16
#define TEGRA210_AMX_MAP_STREAM_NUM_SHIFT 6
#define TEGRA210_AMX_MAP_WORD_NUM_SHIFT 2
#define TEGRA210_AMX_MAP_BYTE_NUM_SHIFT 0
enum {
TEGRA210_AMX_WAIT_ON_ALL,
TEGRA210_AMX_WAIT_ON_ANY,
};
struct tegra210_amx_soc_data {
const struct regmap_config *regmap_conf;
bool auto_disable;
};
struct tegra210_amx {
const struct tegra210_amx_soc_data *soc_data;
unsigned int map[TEGRA210_AMX_RAM_DEPTH];
struct regmap *regmap;
unsigned int byte_mask[2];
};
#endif

View file

@ -0,0 +1,674 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mixer.c - Tegra210 MIXER driver
//
// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra210_mixer.h"
#include "tegra_cif.h"
#define MIXER_REG(reg, id) ((reg) + ((id) * TEGRA210_MIXER_REG_STRIDE))
#define MIXER_REG_BASE(reg) ((reg) % TEGRA210_MIXER_REG_STRIDE)
#define MIXER_GAIN_CFG_RAM_ADDR(id) \
(TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 + \
((id) * TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE))
#define MIXER_RX_REG_DEFAULTS(id) \
{ MIXER_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \
{ MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \
{ MIXER_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0}
#define MIXER_TX_REG_DEFAULTS(id) \
{ MIXER_REG(TEGRA210_MIXER_TX1_INT_MASK, (id)), 0x00000001}, \
{ MIXER_REG(TEGRA210_MIXER_TX1_CIF_CTRL, (id)), 0x00007700}
#define REG_DURATION_PARAM(reg, i) ((reg) + NUM_GAIN_POLY_COEFFS + 1 + (i))
static const struct reg_default tegra210_mixer_reg_defaults[] = {
/* Inputs */
MIXER_RX_REG_DEFAULTS(0),
MIXER_RX_REG_DEFAULTS(1),
MIXER_RX_REG_DEFAULTS(2),
MIXER_RX_REG_DEFAULTS(3),
MIXER_RX_REG_DEFAULTS(4),
MIXER_RX_REG_DEFAULTS(5),
MIXER_RX_REG_DEFAULTS(6),
MIXER_RX_REG_DEFAULTS(7),
MIXER_RX_REG_DEFAULTS(8),
MIXER_RX_REG_DEFAULTS(9),
/* Outputs */
MIXER_TX_REG_DEFAULTS(0),
MIXER_TX_REG_DEFAULTS(1),
MIXER_TX_REG_DEFAULTS(2),
MIXER_TX_REG_DEFAULTS(3),
MIXER_TX_REG_DEFAULTS(4),
{ TEGRA210_MIXER_CG, 0x00000001},
{ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000},
{ TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000},
{ TEGRA210_MIXER_ENABLE, 0x1 },
};
/* Default gain parameters */
static const struct tegra210_mixer_gain_params gain_params = {
/* Polynomial coefficients */
{ 0, 0, 0, 0, 0, 0, 0, 0x1000000, 0 },
/* Gain value */
0x10000,
/* Duration Parameters */
{ 0, 0, 0x400, 0x8000000 },
};
static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev)
{
struct tegra210_mixer *mixer = dev_get_drvdata(dev);
regcache_cache_only(mixer->regmap, true);
regcache_mark_dirty(mixer->regmap);
return 0;
}
static int __maybe_unused tegra210_mixer_runtime_resume(struct device *dev)
{
struct tegra210_mixer *mixer = dev_get_drvdata(dev);
regcache_cache_only(mixer->regmap, false);
regcache_sync(mixer->regmap);
return 0;
}
static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
unsigned int addr,
unsigned int coef)
{
unsigned int reg, val;
int err;
/* Check if busy */
err = regmap_read_poll_timeout(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
val, !(val & 0x80000000), 10, 10000);
if (err < 0)
return err;
reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) &
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN;
regmap_write(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
reg);
regmap_write(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_DATA,
coef);
return 0;
}
static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt,
unsigned int id, bool instant_gain)
{
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = MIXER_GAIN_CFG_RAM_ADDR(id);
int err, i;
pm_runtime_get_sync(cmpnt->dev);
/* Write default gain poly coefficients */
for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) {
err = tegra210_mixer_write_ram(mixer, reg + i,
gain_params.poly_coeff[i]);
if (err < 0)
goto rpm_put;
}
/* Write stored gain value */
err = tegra210_mixer_write_ram(mixer, reg + NUM_GAIN_POLY_COEFFS,
mixer->gain_value[id]);
if (err < 0)
goto rpm_put;
/* Write duration parameters */
for (i = 0; i < NUM_DURATION_PARMS; i++) {
int val;
if (instant_gain)
val = 1;
else
val = gain_params.duration[i];
err = tegra210_mixer_write_ram(mixer,
REG_DURATION_PARAM(reg, i),
val);
if (err < 0)
goto rpm_put;
}
/* Trigger to apply gain configurations */
err = tegra210_mixer_write_ram(mixer, reg + REG_CFG_DONE_TRIGGER,
VAL_CFG_DONE_TRIGGER);
rpm_put:
pm_runtime_put(cmpnt->dev);
return err;
}
static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg;
unsigned int i;
i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
ucontrol->value.integer.value[0] = mixer->gain_value[i];
return 0;
}
static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg, id;
bool instant_gain = false;
int err;
if (strstr(kcontrol->id.name, "Instant Gain Volume"))
instant_gain = true;
/* Save gain value for specific MIXER input */
id = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
mixer->gain_value[id] = ucontrol->value.integer.value[0];
err = tegra210_mixer_configure_gain(cmpnt, id, instant_gain);
if (err) {
dev_err(cmpnt->dev, "Failed to apply gain\n");
return err;
}
return 1;
}
static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
struct snd_pcm_hw_params *params,
unsigned int reg,
unsigned int id)
{
unsigned int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
tegra_set_cif(mixer->regmap,
reg + (id * TEGRA210_MIXER_REG_STRIDE),
&cif_conf);
return 0;
}
static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
int err;
err = tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_RX1_CIF_CTRL,
dai->id);
if (err < 0)
return err;
return tegra210_mixer_configure_gain(dai->component, dai->id, false);
}
static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
return tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_TX1_CIF_CTRL,
dai->id - TEGRA210_MIXER_RX_MAX);
}
static struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = {
.hw_params = tegra210_mixer_out_hw_params,
};
static struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = {
.hw_params = tegra210_mixer_in_hw_params,
};
#define IN_DAI(id) \
{ \
.name = "MIXER-RX-CIF"#id, \
.playback = { \
.stream_name = "RX" #id "-CIF-Playback",\
.channels_min = 1, \
.channels_max = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "RX" #id "-CIF-Capture", \
.channels_min = 1, \
.channels_max = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_mixer_in_dai_ops, \
}
#define OUT_DAI(id) \
{ \
.name = "MIXER-TX-CIF" #id, \
.playback = { \
.stream_name = "TX" #id "-CIF-Playback",\
.channels_min = 1, \
.channels_max = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = "TX" #id "-CIF-Capture", \
.channels_min = 1, \
.channels_max = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = &tegra210_mixer_out_dai_ops, \
}
static struct snd_soc_dai_driver tegra210_mixer_dais[] = {
/* Mixer Input */
IN_DAI(1),
IN_DAI(2),
IN_DAI(3),
IN_DAI(4),
IN_DAI(5),
IN_DAI(6),
IN_DAI(7),
IN_DAI(8),
IN_DAI(9),
IN_DAI(10),
/* Mixer Output */
OUT_DAI(1),
OUT_DAI(2),
OUT_DAI(3),
OUT_DAI(4),
OUT_DAI(5),
};
#define ADDER_CTRL_DECL(name, reg) \
static const struct snd_kcontrol_new name[] = { \
SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0), \
SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0), \
SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0), \
SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0), \
SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0), \
SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0), \
SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0), \
SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0), \
SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0), \
SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0), \
}
ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG);
ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG);
ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG);
ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);
ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);
#define GAIN_CTRL(id) \
SOC_SINGLE_EXT("RX" #id " Gain Volume", \
MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
0x20000, 0, tegra210_mixer_get_gain, \
tegra210_mixer_put_gain), \
SOC_SINGLE_EXT("RX" #id " Instant Gain Volume", \
MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
0x20000, 0, tegra210_mixer_get_gain, \
tegra210_mixer_put_gain),
/* Volume controls for all MIXER inputs */
static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = {
GAIN_CTRL(1)
GAIN_CTRL(2)
GAIN_CTRL(3)
GAIN_CTRL(4)
GAIN_CTRL(5)
GAIN_CTRL(6)
GAIN_CTRL(7)
GAIN_CTRL(8)
GAIN_CTRL(9)
GAIN_CTRL(10)
};
static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {
SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0),
SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0, adder1,
ARRAY_SIZE(adder1)),
SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0, adder2,
ARRAY_SIZE(adder2)),
SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0, adder3,
ARRAY_SIZE(adder3)),
SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0, adder4,
ARRAY_SIZE(adder4)),
SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0, adder5,
ARRAY_SIZE(adder5)),
};
#define RX_ROUTES(id, sname) \
{ "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \
{ "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \
{ "RX" #id, NULL, "RX" #id "-CIF-" sname }
#define MIXER_RX_ROUTES(id) \
RX_ROUTES(id, "Playback"), \
RX_ROUTES(id, "Capture")
#define ADDER_ROUTES(id, sname) \
{ "Adder" #id, "RX1", "RX1" }, \
{ "Adder" #id, "RX2", "RX2" }, \
{ "Adder" #id, "RX3", "RX3" }, \
{ "Adder" #id, "RX4", "RX4" }, \
{ "Adder" #id, "RX5", "RX5" }, \
{ "Adder" #id, "RX6", "RX6" }, \
{ "Adder" #id, "RX7", "RX7" }, \
{ "Adder" #id, "RX8", "RX8" }, \
{ "Adder" #id, "RX9", "RX9" }, \
{ "Adder" #id, "RX10", "RX10" }, \
{ "TX" #id, NULL, "Adder" #id }, \
{ "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
{ "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
{ "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } \
#define TX_ROUTES(id, sname) \
ADDER_ROUTES(1, sname), \
ADDER_ROUTES(2, sname), \
ADDER_ROUTES(3, sname), \
ADDER_ROUTES(4, sname), \
ADDER_ROUTES(5, sname)
#define MIXER_TX_ROUTES(id) \
TX_ROUTES(id, "Playback"), \
TX_ROUTES(id, "Capture")
static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {
/* Input */
MIXER_RX_ROUTES(1),
MIXER_RX_ROUTES(2),
MIXER_RX_ROUTES(3),
MIXER_RX_ROUTES(4),
MIXER_RX_ROUTES(5),
MIXER_RX_ROUTES(6),
MIXER_RX_ROUTES(7),
MIXER_RX_ROUTES(8),
MIXER_RX_ROUTES(9),
MIXER_RX_ROUTES(10),
/* Output */
MIXER_TX_ROUTES(1),
MIXER_TX_ROUTES(2),
MIXER_TX_ROUTES(3),
MIXER_TX_ROUTES(4),
MIXER_TX_ROUTES(5),
};
static const struct snd_soc_component_driver tegra210_mixer_cmpnt = {
.dapm_widgets = tegra210_mixer_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_mixer_widgets),
.dapm_routes = tegra210_mixer_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_mixer_routes),
.controls = tegra210_mixer_gain_ctls,
.num_controls = ARRAY_SIZE(tegra210_mixer_gain_ctls),
};
static bool tegra210_mixer_wr_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg = MIXER_REG_BASE(reg);
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET:
case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL:
case TEGRA210_MIXER_TX1_ENABLE:
case TEGRA210_MIXER_TX1_SOFT_RESET:
case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG:
case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL:
return true;
default:
return false;
}
}
static bool tegra210_mixer_rd_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg = MIXER_REG_BASE(reg);
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT:
case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL:
return true;
default:
return false;
}
}
static bool tegra210_mixer_volatile_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg = MIXER_REG_BASE(reg);
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET:
case TEGRA210_MIXER_RX1_STATUS:
case TEGRA210_MIXER_TX1_SOFT_RESET:
case TEGRA210_MIXER_TX1_STATUS:
case TEGRA210_MIXER_TX1_INT_STATUS:
case TEGRA210_MIXER_TX1_INT_SET:
case TEGRA210_MIXER_SOFT_RESET:
case TEGRA210_MIXER_STATUS:
case TEGRA210_MIXER_INT_STATUS:
case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL:
case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
case TEGRA210_MIXER_PEAKM_RAM_CTRL:
case TEGRA210_MIXER_PEAKM_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_mixer_precious_reg(struct device *dev,
unsigned int reg)
{
switch (reg) {
case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
case TEGRA210_MIXER_PEAKM_RAM_DATA:
return true;
default:
return false;
}
}
static const struct regmap_config tegra210_mixer_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_MIXER_CTRL,
.writeable_reg = tegra210_mixer_wr_reg,
.readable_reg = tegra210_mixer_rd_reg,
.volatile_reg = tegra210_mixer_volatile_reg,
.precious_reg = tegra210_mixer_precious_reg,
.reg_defaults = tegra210_mixer_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra210_mixer_of_match[] = {
{ .compatible = "nvidia,tegra210-amixer" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
static int tegra210_mixer_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_mixer *mixer;
void __iomem *regs;
int err, i;
mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
if (!mixer)
return -ENOMEM;
dev_set_drvdata(dev, mixer);
/* Use default gain value for all MIXER inputs */
for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
mixer->gain_value[i] = gain_params.gain_value;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
mixer->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_mixer_regmap_config);
if (IS_ERR(mixer->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(mixer->regmap);
}
regcache_cache_only(mixer->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt,
tegra210_mixer_dais,
ARRAY_SIZE(tegra210_mixer_dais));
if (err) {
dev_err(dev, "can't register MIXER component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_mixer_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_mixer_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend,
tegra210_mixer_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_mixer_driver = {
.driver = {
.name = "tegra210_mixer",
.of_match_table = tegra210_mixer_of_match,
.pm = &tegra210_mixer_pm_ops,
},
.probe = tegra210_mixer_platform_probe,
.remove = tegra210_mixer_platform_remove,
};
module_platform_driver(tegra210_mixer_driver);
MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_mixer.h - Definitions for Tegra210 MIXER driver
*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_MIXER_H__
#define __TEGRA210_MIXER_H__
/* XBAR_RX related MIXER offsets */
#define TEGRA210_MIXER_RX1_SOFT_RESET 0x04
#define TEGRA210_MIXER_RX1_STATUS 0x10
#define TEGRA210_MIXER_RX1_CIF_CTRL 0x24
#define TEGRA210_MIXER_RX1_CTRL 0x28
#define TEGRA210_MIXER_RX1_PEAK_CTRL 0x2c
#define TEGRA210_MIXER_RX1_SAMPLE_COUNT 0x30
/* XBAR_TX related MIXER offsets */
#define TEGRA210_MIXER_TX1_ENABLE 0x280
#define TEGRA210_MIXER_TX1_SOFT_RESET 0x284
#define TEGRA210_MIXER_TX1_STATUS 0x290
#define TEGRA210_MIXER_TX1_INT_STATUS 0x294
#define TEGRA210_MIXER_TX1_INT_MASK 0x298
#define TEGRA210_MIXER_TX1_INT_SET 0x29c
#define TEGRA210_MIXER_TX1_INT_CLEAR 0x2a0
#define TEGRA210_MIXER_TX1_CIF_CTRL 0x2a4
#define TEGRA210_MIXER_TX1_ADDER_CONFIG 0x2a8
/* MIXER related offsets */
#define TEGRA210_MIXER_ENABLE 0x400
#define TEGRA210_MIXER_SOFT_RESET 0x404
#define TEGRA210_MIXER_CG 0x408
#define TEGRA210_MIXER_STATUS 0x410
#define TEGRA210_MIXER_INT_STATUS 0x414
#define TEGRA210_MIXER_GAIN_CFG_RAM_CTRL 0x42c
#define TEGRA210_MIXER_GAIN_CFG_RAM_DATA 0x430
#define TEGRA210_MIXER_PEAKM_RAM_CTRL 0x434
#define TEGRA210_MIXER_PEAKM_RAM_DATA 0x438
#define TEGRA210_MIXER_CTRL 0x43c
#define TEGRA210_MIXER_TX2_ADDER_CONFIG (TEGRA210_MIXER_TX1_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX3_ADDER_CONFIG (TEGRA210_MIXER_TX2_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX4_ADDER_CONFIG (TEGRA210_MIXER_TX3_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX5_ADDER_CONFIG (TEGRA210_MIXER_TX4_ADDER_CONFIG + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX2_ENABLE (TEGRA210_MIXER_TX1_ENABLE + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX3_ENABLE (TEGRA210_MIXER_TX2_ENABLE + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX4_ENABLE (TEGRA210_MIXER_TX3_ENABLE + TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX5_ENABLE (TEGRA210_MIXER_TX4_ENABLE + TEGRA210_MIXER_REG_STRIDE)
/* Fields in TEGRA210_MIXER_ENABLE */
#define TEGRA210_MIXER_ENABLE_SHIFT 0
#define TEGRA210_MIXER_ENABLE_MASK (1 << TEGRA210_MIXER_ENABLE_SHIFT)
#define TEGRA210_MIXER_EN (1 << TEGRA210_MIXER_ENABLE_SHIFT)
/* Fields in TEGRA210_MIXER_GAIN_CFG_RAM_CTRL */
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 0x0
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE 0x10
#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT 14
#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE (1 << TEGRA210_MIXER_GAIN_CFG_RAM_RW_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT 12
#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_MASK (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN (1 << TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT 0
#define TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK (0x1ff << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT)
#define TEGRA210_MIXER_REG_STRIDE 0x40
#define TEGRA210_MIXER_RX_MAX 10
#define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_REG_STRIDE)
#define TEGRA210_MIXER_TX_MAX 5
#define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_REG_STRIDE))
#define REG_CFG_DONE_TRIGGER 0xf
#define VAL_CFG_DONE_TRIGGER 0x1
#define NUM_GAIN_POLY_COEFFS 9
#define NUM_DURATION_PARMS 4
struct tegra210_mixer_gain_params {
int poly_coeff[NUM_GAIN_POLY_COEFFS];
int gain_value;
int duration[NUM_DURATION_PARMS];
};
struct tegra210_mixer {
int gain_value[TEGRA210_MIXER_RX_MAX];
struct regmap *regmap;
};
#endif

View file

@ -0,0 +1,645 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mvc.c - Tegra210 MVC driver
//
// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra210_mvc.h"
#include "tegra_cif.h"
static const struct reg_default tegra210_mvc_reg_defaults[] = {
{ TEGRA210_MVC_RX_INT_MASK, 0x00000001},
{ TEGRA210_MVC_RX_CIF_CTRL, 0x00007700},
{ TEGRA210_MVC_TX_INT_MASK, 0x00000001},
{ TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
{ TEGRA210_MVC_CG, 0x1},
{ TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
{ TEGRA210_MVC_INIT_VOL, 0x00800000},
{ TEGRA210_MVC_TARGET_VOL, 0x00800000},
{ TEGRA210_MVC_DURATION, 0x000012c0},
{ TEGRA210_MVC_DURATION_INV, 0x0006d3a0},
{ TEGRA210_MVC_POLY_N1, 0x0000007d},
{ TEGRA210_MVC_POLY_N2, 0x00000271},
{ TEGRA210_MVC_PEAK_CTRL, 0x000012c0},
{ TEGRA210_MVC_CFG_RAM_CTRL, 0x00004000},
};
static const struct tegra210_mvc_gain_params gain_params = {
.poly_coeff = { 23738319, 659403, -3680,
15546680, 2530732, -120985,
12048422, 5527252, -785042 },
.poly_n1 = 16,
.poly_n2 = 63,
.duration = 150,
.duration_inv = 14316558,
};
static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev)
{
struct tegra210_mvc *mvc = dev_get_drvdata(dev);
regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &(mvc->ctrl_value));
regcache_cache_only(mvc->regmap, true);
regcache_mark_dirty(mvc->regmap);
return 0;
}
static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev)
{
struct tegra210_mvc *mvc = dev_get_drvdata(dev);
regcache_cache_only(mvc->regmap, false);
regcache_sync(mvc->regmap);
regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value);
regmap_update_bits(mvc->regmap,
TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
return 0;
}
static void tegra210_mvc_write_ram(struct regmap *regmap)
{
int i;
regmap_write(regmap, TEGRA210_MVC_CFG_RAM_CTRL,
TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE);
for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++)
regmap_write(regmap, TEGRA210_MVC_CFG_RAM_DATA,
gain_params.poly_coeff[i]);
}
static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
{
/*
* Volume control read from mixer control is with
* 100x scaling; for CURVE_POLY the reg range
* is 0-100 (linear, Q24) and for CURVE_LINEAR
* it is -120dB to +40dB (Q8)
*/
if (mvc->curve_type == CURVE_POLY) {
if (val > 10000)
val = 10000;
mvc->volume[chan] = ((val * (1<<8)) / 100) << 16;
} else {
val -= 12000;
mvc->volume[chan] = (val * (1<<8)) / 100;
}
}
static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
u8 mute_mask;
u32 val;
pm_runtime_get_sync(cmpnt->dev);
regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val);
pm_runtime_put(cmpnt->dev);
mute_mask = (val >> TEGRA210_MVC_MUTE_SHIFT) &
TEGRA210_MUTE_MASK_EN;
ucontrol->value.integer.value[0] = mute_mask;
return 0;
}
static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
unsigned int value;
u8 mute_mask;
int err;
pm_runtime_get_sync(cmpnt->dev);
/* Check if VOLUME_SWITCH is triggered */
err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
10, 10000);
if (err < 0)
goto end;
mute_mask = ucontrol->value.integer.value[0];
err = regmap_update_bits(mvc->regmap, mc->reg,
TEGRA210_MVC_MUTE_MASK,
mute_mask << TEGRA210_MVC_MUTE_SHIFT);
if (err < 0)
goto end;
return 1;
end:
pm_runtime_put(cmpnt->dev);
return err;
}
static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
s32 val = mvc->volume[chan];
if (mvc->curve_type == CURVE_POLY) {
val = ((val >> 16) * 100) >> 8;
} else {
val = (val * 100) >> 8;
val += 12000;
}
ucontrol->value.integer.value[0] = val;
return 0;
}
static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg;
unsigned int value;
u8 chan;
int err;
pm_runtime_get_sync(cmpnt->dev);
/* Check if VOLUME_SWITCH is triggered */
err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
10, 10000);
if (err < 0)
goto end;
chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE;
tegra210_mvc_conv_vol(mvc, chan,
ucontrol->value.integer.value[0]);
/* Configure init volume same as target volume */
regmap_write(mvc->regmap,
TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
mvc->volume[chan]);
regmap_write(mvc->regmap, reg, mvc->volume[chan]);
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
return 1;
end:
pm_runtime_put(cmpnt->dev);
return err;
}
static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
struct device *dev)
{
int i;
/* Change volume to default init for new curve type */
if (mvc->curve_type == CURVE_POLY) {
for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
} else {
for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
}
pm_runtime_get_sync(dev);
/* Program curve type */
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_CURVE_TYPE_MASK,
mvc->curve_type <<
TEGRA210_MVC_CURVE_TYPE_SHIFT);
/* Init volume for all channels */
for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) {
regmap_write(mvc->regmap,
TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i),
mvc->volume[i]);
regmap_write(mvc->regmap,
TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i),
mvc->volume[i]);
}
/* Trigger volume switch */
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
pm_runtime_put(dev);
}
static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
ucontrol->value.integer.value[0] = mvc->curve_type;
return 0;
}
static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
int value;
regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value);
if (value & TEGRA210_MVC_EN) {
dev_err(cmpnt->dev,
"Curve type can't be set when MVC is running\n");
return -EINVAL;
}
if (mvc->curve_type == ucontrol->value.integer.value[0])
return 0;
mvc->curve_type = ucontrol->value.integer.value[0];
tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
return 1;
}
static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
struct snd_pcm_hw_params *params,
unsigned int reg)
{
unsigned int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
tegra_set_cif(mvc->regmap, reg, &cif_conf);
return 0;
}
static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct device *dev = dai->dev;
struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
int err, val;
/*
* Soft Reset: Below performs module soft reset which clears
* all FSM logic, flushes flow control of FIFO and resets the
* state register. It also brings module back to disabled
* state (without flushing the data in the pipe).
*/
regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET,
val, !val, 10, 10000);
if (err < 0) {
dev_err(dev, "SW reset failed, err = %d\n", err);
return err;
}
/* Set RX CIF */
err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL);
if (err) {
dev_err(dev, "Can't set MVC RX CIF: %d\n", err);
return err;
}
/* Set TX CIF */
err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL);
if (err) {
dev_err(dev, "Can't set MVC TX CIF: %d\n", err);
return err;
}
tegra210_mvc_write_ram(mvc->regmap);
/* Program poly_n1, poly_n2, duration */
regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1);
regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2);
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration);
/* Program duration_inv */
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV,
gain_params.duration_inv);
return 0;
}
static struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
.hw_params = tegra210_mvc_hw_params,
};
static const char * const tegra210_mvc_curve_type_text[] = {
"Poly",
"Linear",
};
static const struct soc_enum tegra210_mvc_curve_type_ctrl =
SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text);
#define TEGRA210_MVC_VOL_CTRL(chan) \
SOC_SINGLE_EXT("Channel" #chan " Volume", \
TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \
(chan - 1)), \
0, 16000, 0, tegra210_mvc_get_vol, \
tegra210_mvc_put_vol)
static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
/* Per channel volume control */
TEGRA210_MVC_VOL_CTRL(1),
TEGRA210_MVC_VOL_CTRL(2),
TEGRA210_MVC_VOL_CTRL(3),
TEGRA210_MVC_VOL_CTRL(4),
TEGRA210_MVC_VOL_CTRL(5),
TEGRA210_MVC_VOL_CTRL(6),
TEGRA210_MVC_VOL_CTRL(7),
TEGRA210_MVC_VOL_CTRL(8),
/* Per channel mute */
SOC_SINGLE_EXT("Per Chan Mute Mask",
TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
tegra210_mvc_get_mute, tegra210_mvc_put_mute),
SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
};
static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
/* Input */
{
.name = "MVC-RX-CIF",
.playback = {
.stream_name = "RX-CIF-Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "RX-CIF-Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
/* Output */
{
.name = "MVC-TX-CIF",
.playback = {
.stream_name = "TX-CIF-Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "TX-CIF-Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &tegra210_mvc_dai_ops,
}
};
static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = {
SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE,
TEGRA210_MVC_EN_SHIFT, 0),
};
#define MVC_ROUTES(sname) \
{ "RX XBAR-" sname, NULL, "XBAR-TX" }, \
{ "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \
{ "RX", NULL, "RX-CIF-" sname }, \
{ "TX-CIF-" sname, NULL, "TX" }, \
{ "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \
{ "XBAR-RX", NULL, "TX XBAR-" sname }
static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
{ "TX", NULL, "RX" },
MVC_ROUTES("Playback"),
MVC_ROUTES("Capture"),
};
static const struct snd_soc_component_driver tegra210_mvc_cmpnt = {
.dapm_widgets = tegra210_mvc_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets),
.dapm_routes = tegra210_mvc_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes),
.controls = tegra210_mvc_vol_ctrl,
.num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl),
};
static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE:
return true;
default:
return false;
};
}
static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL:
case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL:
case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG:
case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_MVC_RX_STATUS:
case TEGRA210_MVC_RX_INT_STATUS:
case TEGRA210_MVC_RX_INT_SET:
case TEGRA210_MVC_TX_STATUS:
case TEGRA210_MVC_TX_INT_STATUS:
case TEGRA210_MVC_TX_INT_SET:
case TEGRA210_MVC_SOFT_RESET:
case TEGRA210_MVC_STATUS:
case TEGRA210_MVC_INT_STATUS:
case TEGRA210_MVC_SWITCH:
case TEGRA210_MVC_CFG_RAM_CTRL:
case TEGRA210_MVC_CFG_RAM_DATA:
case TEGRA210_MVC_PEAK_VALUE:
case TEGRA210_MVC_CTRL:
return true;
default:
return false;
}
}
static const struct regmap_config tegra210_mvc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_MVC_CONFIG_ERR_TYPE,
.writeable_reg = tegra210_mvc_wr_reg,
.readable_reg = tegra210_mvc_rd_reg,
.volatile_reg = tegra210_mvc_volatile_reg,
.reg_defaults = tegra210_mvc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra210_mvc_of_match[] = {
{ .compatible = "nvidia,tegra210-mvc" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
static int tegra210_mvc_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_mvc *mvc;
void __iomem *regs;
int err;
mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL);
if (!mvc)
return -ENOMEM;
dev_set_drvdata(dev, mvc);
mvc->curve_type = CURVE_LINEAR;
mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
mvc->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_mvc_regmap_config);
if (IS_ERR(mvc->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(mvc->regmap);
}
regcache_cache_only(mvc->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt,
tegra210_mvc_dais,
ARRAY_SIZE(tegra210_mvc_dais));
if (err) {
dev_err(dev, "can't register MVC component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
return 0;
}
static int tegra210_mvc_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_mvc_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend,
tegra210_mvc_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_mvc_driver = {
.driver = {
.name = "tegra210-mvc",
.of_match_table = tegra210_mvc_of_match,
.pm = &tegra210_mvc_pm_ops,
},
.probe = tegra210_mvc_platform_probe,
.remove = tegra210_mvc_platform_remove,
};
module_platform_driver(tegra210_mvc_driver)
MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_mvc.h - Definitions for Tegra210 MVC driver
*
* Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_MVC_H__
#define __TEGRA210_MVC_H__
/*
* MVC_RX registers are with respect to XBAR.
* The data comes from XBAR to MVC.
*/
#define TEGRA210_MVC_RX_STATUS 0x0c
#define TEGRA210_MVC_RX_INT_STATUS 0x10
#define TEGRA210_MVC_RX_INT_MASK 0x14
#define TEGRA210_MVC_RX_INT_SET 0x18
#define TEGRA210_MVC_RX_INT_CLEAR 0x1c
#define TEGRA210_MVC_RX_CIF_CTRL 0x20
/*
* MVC_TX registers are with respect to XBAR.
* The data goes out of MVC.
*/
#define TEGRA210_MVC_TX_STATUS 0x4c
#define TEGRA210_MVC_TX_INT_STATUS 0x50
#define TEGRA210_MVC_TX_INT_MASK 0x54
#define TEGRA210_MVC_TX_INT_SET 0x58
#define TEGRA210_MVC_TX_INT_CLEAR 0x5c
#define TEGRA210_MVC_TX_CIF_CTRL 0x60
/* Register offsets from TEGRA210_MVC*_BASE */
#define TEGRA210_MVC_ENABLE 0x80
#define TEGRA210_MVC_SOFT_RESET 0x84
#define TEGRA210_MVC_CG 0x88
#define TEGRA210_MVC_STATUS 0x90
#define TEGRA210_MVC_INT_STATUS 0x94
#define TEGRA210_MVC_CTRL 0xa8
#define TEGRA210_MVC_SWITCH 0xac
#define TEGRA210_MVC_INIT_VOL 0xb0
#define TEGRA210_MVC_TARGET_VOL 0xd0
#define TEGRA210_MVC_DURATION 0xf0
#define TEGRA210_MVC_DURATION_INV 0xf4
#define TEGRA210_MVC_POLY_N1 0xf8
#define TEGRA210_MVC_POLY_N2 0xfc
#define TEGRA210_MVC_PEAK_CTRL 0x100
#define TEGRA210_MVC_CFG_RAM_CTRL 0x104
#define TEGRA210_MVC_CFG_RAM_DATA 0x108
#define TEGRA210_MVC_PEAK_VALUE 0x10c
#define TEGRA210_MVC_CONFIG_ERR_TYPE 0x12c
/* Fields in TEGRA210_MVC_ENABLE */
#define TEGRA210_MVC_EN_SHIFT 0
#define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT)
#define TEGRA210_MVC_MUTE_SHIFT 8
#define TEGRA210_MUTE_MASK_EN 0xff
#define TEGRA210_MVC_MUTE_MASK (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
#define TEGRA210_MVC_MUTE_EN (TEGRA210_MUTE_MASK_EN << TEGRA210_MVC_MUTE_SHIFT)
#define TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT 30
#define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
#define TEGRA210_MVC_PER_CHAN_CTRL_EN (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
#define TEGRA210_MVC_CURVE_TYPE_SHIFT 1
#define TEGRA210_MVC_CURVE_TYPE_MASK (1 << TEGRA210_MVC_CURVE_TYPE_SHIFT)
#define TEGRA210_MVC_VOLUME_SWITCH_SHIFT 2
#define TEGRA210_MVC_VOLUME_SWITCH_MASK (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT)
#define TEGRA210_MVC_VOLUME_SWITCH_TRIGGER (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT)
#define TEGRA210_MVC_CTRL_DEFAULT 0x40000003
#define TEGRA210_MVC_INIT_VOL_DEFAULT_POLY 0x01000000
#define TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR 0x00000000
/* Fields in TEGRA210_MVC ram ctrl */
#define TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT 14
#define TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_MVC_CFG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
#define TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT 0
#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK (0x1ff << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT)
#define REG_SIZE 4
#define TEGRA210_MVC_MAX_CHAN_COUNT 8
#define TEGRA210_MVC_REG_OFFSET(reg, i) (reg + (REG_SIZE * i))
#define NUM_GAIN_POLY_COEFFS 9
enum {
CURVE_POLY,
CURVE_LINEAR,
};
struct tegra210_mvc_gain_params {
int poly_coeff[NUM_GAIN_POLY_COEFFS];
int poly_n1;
int poly_n2;
int duration;
int duration_inv;
};
struct tegra210_mvc {
int volume[TEGRA210_MVC_MAX_CHAN_COUNT];
unsigned int curve_type;
unsigned int ctrl_value;
struct regmap *regmap;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_sfc.h - Definitions for Tegra210 SFC driver
*
* Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_SFC_H__
#define __TEGRA210_SFC_H__
/*
* SFC_RX registers are with respect to XBAR.
* The data comes from XBAR to SFC.
*/
#define TEGRA210_SFC_RX_STATUS 0x0c
#define TEGRA210_SFC_RX_INT_STATUS 0x10
#define TEGRA210_SFC_RX_INT_MASK 0x14
#define TEGRA210_SFC_RX_INT_SET 0x18
#define TEGRA210_SFC_RX_INT_CLEAR 0x1c
#define TEGRA210_SFC_RX_CIF_CTRL 0x20
#define TEGRA210_SFC_RX_FREQ 0x24
/*
* SFC_TX registers are with respect to XBAR.
* The data goes out of SFC.
*/
#define TEGRA210_SFC_TX_STATUS 0x4c
#define TEGRA210_SFC_TX_INT_STATUS 0x50
#define TEGRA210_SFC_TX_INT_MASK 0x54
#define TEGRA210_SFC_TX_INT_SET 0x58
#define TEGRA210_SFC_TX_INT_CLEAR 0x5c
#define TEGRA210_SFC_TX_CIF_CTRL 0x60
#define TEGRA210_SFC_TX_FREQ 0x64
/* Register offsets from TEGRA210_SFC*_BASE */
#define TEGRA210_SFC_ENABLE 0x80
#define TEGRA210_SFC_SOFT_RESET 0x84
#define TEGRA210_SFC_CG 0x88
#define TEGRA210_SFC_STATUS 0x8c
#define TEGRA210_SFC_INT_STATUS 0x90
#define TEGRA210_SFC_COEF_RAM 0xbc
#define TEGRA210_SFC_CFG_RAM_CTRL 0xc0
#define TEGRA210_SFC_CFG_RAM_DATA 0xc4
/* Fields in TEGRA210_SFC_ENABLE */
#define TEGRA210_SFC_EN_SHIFT 0
#define TEGRA210_SFC_EN (1 << TEGRA210_SFC_EN_SHIFT)
#define TEGRA210_SFC_NUM_RATES 12
/* Fields in TEGRA210_SFC_COEF_RAM */
#define TEGRA210_SFC_COEF_RAM_EN BIT(0)
#define TEGRA210_SFC_SOFT_RESET_EN BIT(0)
/* Coefficients */
#define TEGRA210_SFC_COEF_RAM_DEPTH 64
#define TEGRA210_SFC_RAM_CTRL_RW_WRITE (1 << 14)
#define TEGRA210_SFC_RAM_CTRL_ADDR_INIT_EN (1 << 13)
#define TEGRA210_SFC_RAM_CTRL_SEQ_ACCESS_EN (1 << 12)
enum tegra210_sfc_path {
SFC_RX_PATH,
SFC_TX_PATH,
SFC_PATHS,
};
struct tegra210_sfc {
unsigned int mono_to_stereo[SFC_PATHS];
unsigned int stereo_to_mono[SFC_PATHS];
unsigned int srate_out;
unsigned int srate_in;
struct regmap *regmap;
};
#endif