Revert "ASoC: tegra: Remove legacy AHUB OOT drivers"

This reverts commit 289095dfc6.
This is done to unblock gvs intermittency of audio test and
kernel warning test failure.

Bug 4508166

Change-Id: I989c0467fb5f2d962fb980085a1652df892128f6
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3082635
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Tested-by: Shubham Chandra <shubhamc@nvidia.com>
Reviewed-by: Shubham Chandra <shubhamc@nvidia.com>
This commit is contained in:
Sameer Pujar
2024-02-23 11:01:02 +00:00
committed by Shubham Chandra
parent ed7a856cf7
commit 00c10d965b
33 changed files with 17091 additions and 7 deletions

View File

@@ -1,17 +1,42 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION. All rights reserved. # Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
snd-soc-tegra-utils-oot-objs := tegra_asoc_utils.o tegra_asoc_machine.o \ snd-soc-tegra-utils-oot-objs := tegra_asoc_utils.o tegra_asoc_machine.o \
tegra_isomgr_bw.o tegra_codecs.o tegra_isomgr_bw.o tegra_codecs.o
snd-soc-tegra210-ahub-oot-objs := tegra210_ahub.o
snd-soc-tegra210-dmic-oot-objs := tegra210_dmic.o
snd-soc-tegra210-i2s-oot-objs := tegra210_i2s.o
snd-soc-tegra186-dspk-oot-objs := tegra186_dspk.o
snd-soc-tegra210-admaif-oot-objs := tegra210_admaif.o
snd-soc-tegra210-amx-oot-objs := tegra210_amx.o
snd-soc-tegra210-adx-oot-objs := tegra210_adx.o
snd-soc-tegra210-mixer-oot-objs := tegra210_mixer.o
snd-soc-tegra210-sfc-oot-objs := tegra210_sfc.o
snd-soc-tegra210-afc-oot-objs := tegra210_afc.o snd-soc-tegra210-afc-oot-objs := tegra210_afc.o
snd-soc-tegra210-mvc-oot-objs := tegra210_mvc.o
snd-soc-tegra210-iqc-oot-objs := tegra210_iqc.o snd-soc-tegra210-iqc-oot-objs := tegra210_iqc.o
snd-soc-tegra186-asrc-oot-objs := tegra186_asrc.o
snd-soc-tegra186-arad-oot-objs := tegra186_arad.o snd-soc-tegra186-arad-oot-objs := tegra186_arad.o
snd-soc-tegra210-ope-oot-objs := tegra210_ope.o tegra210_peq.o \
tegra210_mbdrc.o
snd-soc-tegra-machine-driver-oot-objs := tegra_machine_driver.o snd-soc-tegra-machine-driver-oot-objs := tegra_machine_driver.o
snd-soc-tegra-controls-objs := tegra_mixer_control.o snd-soc-tegra-controls-objs := tegra_mixer_control.o
obj-m += snd-soc-tegra-utils-oot.o obj-m += snd-soc-tegra-utils-oot.o
obj-m += snd-soc-tegra210-dmic-oot.o
obj-m += snd-soc-tegra210-ahub-oot.o
obj-m += snd-soc-tegra210-i2s-oot.o
obj-m += snd-soc-tegra186-dspk-oot.o
obj-m += snd-soc-tegra210-admaif-oot.o
obj-m += snd-soc-tegra210-amx-oot.o
obj-m += snd-soc-tegra210-adx-oot.o
obj-m += snd-soc-tegra210-mixer-oot.o
obj-m += snd-soc-tegra210-sfc-oot.o
obj-m += snd-soc-tegra210-afc-oot.o obj-m += snd-soc-tegra210-afc-oot.o
obj-m += snd-soc-tegra210-mvc-oot.o
obj-m += snd-soc-tegra210-iqc-oot.o obj-m += snd-soc-tegra210-iqc-oot.o
obj-m += snd-soc-tegra210-ope-oot.o
obj-m += snd-soc-tegra186-arad-oot.o obj-m += snd-soc-tegra186-arad-oot.o
obj-m += snd-soc-tegra186-asrc-oot.o
obj-m += snd-soc-tegra-machine-driver-oot.o obj-m += snd-soc-tegra-machine-driver-oot.o
obj-m += snd-soc-tegra-controls.o obj-m += snd-soc-tegra-controls.o

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2015-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// //
// tegra186_arad.c - Tegra186 ARAD driver // tegra186_arad.c - Tegra186 ARAD driver
//
// Copyright (c) 2015-2023, NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h> #include <nvidia/conftest.h>
@@ -26,10 +27,7 @@
#include <drivers-private/sound/soc/tegra/tegra_cif.h> #include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra186_arad.h" #include "tegra186_arad.h"
#ifdef CONFIG_SND_SOC_TEGRA186_ASRC_WAR
#include "tegra186_asrc.h" #include "tegra186_asrc.h"
#endif
static struct device *arad_dev; static struct device *arad_dev;

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra186_asrc.h - Definitions for Tegra186 ASRC driver
*
* Copyright (c) 2015-2022, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA186_ASRC_H__
#define __TEGRA186_ASRC_H__
#define TEGRA186_ASRC_STREAM_STRIDE 0x80
#define TEGRA186_ASRC_STREAM_MAX 6
#define TEGRA186_ASRC_STREAM_LIMIT 0x2f0
/* ASRC stream related offset */
#define TEGRA186_ASRC_STREAM1_CONFIG 0x0
#define TEGRA186_ASRC_STREAM1_RATIO_INTEGER_PART 0x4
#define TEGRA186_ASRC_STREAM1_RATIO_FRAC_PART 0x8
#define TEGRA186_ASRC_STREAM1_RATIO_LOCK_STATUS 0xc
#define TEGRA186_ASRC_STREAM1_MUTE_UNMUTE_DURATION 0x10
#define TEGRA186_ASRC_STREAM1_TX_THRESHOLD 0x14
#define TEGRA186_ASRC_STREAM1_RX_THRESHOLD 0x18
#define TEGRA186_ASRC_STREAM1_RATIO_COMP 0x1C
#define TEGRA186_ASRC_STREAM1_RX_STATUS 0x20
#define TEGRA186_ASRC_STREAM1_RX_CIF_CTRL 0x24
#define TEGRA186_ASRC_STREAM1_TX_STATUS 0x2c
#define TEGRA186_ASRC_STREAM1_TX_CIF_CTRL 0x30
#define TEGRA186_ASRC_STREAM1_ENABLE 0x38
#define TEGRA186_ASRC_STREAM1_SOFT_RESET 0x3c
#define TEGRA186_ASRC_STREAM1_STATUS 0x4c
#define TEGRA186_ASRC_STREAM1_BUFFER_STATUS 0x50
#define TEGRA186_ASRC_STREAM1_CONFIG_ERR_TYPE 0x54
#define TEGRA186_ASRC_STREAM1_STATEBUF_ADDR 0x5c
#define TEGRA186_ASRC_STREAM1_STATEBUF_CONFIG 0x60
#define TEGRA186_ASRC_STREAM1_INSAMPLEBUF_ADDR 0x64
#define TEGRA186_ASRC_STREAM1_INSAMPLEBUF_CONFIG 0x68
#define TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_ADDR 0x6c
#define TEGRA186_ASRC_STREAM1_OUTSAMPLEBUF_CONFIG 0x70
#define TEGRA186_ASRC_STREAM2_RATIO_INTEGER_PART 0x84
#define TEGRA186_ASRC_STREAM2_RATIO_FRAC_PART 0x88
#define TEGRA186_ASRC_STREAM3_RATIO_INTEGER_PART 0x104
#define TEGRA186_ASRC_STREAM3_RATIO_FRAC_PART 0x108
#define TEGRA186_ASRC_STREAM4_RATIO_INTEGER_PART 0x184
#define TEGRA186_ASRC_STREAM4_RATIO_FRAC_PART 0x188
#define TEGRA186_ASRC_STREAM5_RATIO_INTEGER_PART 0x204
#define TEGRA186_ASRC_STREAM5_RATIO_FRAC_PART 0x208
#define TEGRA186_ASRC_STREAM6_RATIO_INTEGER_PART 0x284
#define TEGRA186_ASRC_STREAM6_RATIO_FRAC_PART 0x288
#define TEGRA186_ASRC_STREAM1_ENABLE 0x38
#define TEGRA186_ASRC_STREAM2_ENABLE 0xb8
#define TEGRA186_ASRC_STREAM3_ENABLE 0x138
#define TEGRA186_ASRC_STREAM4_ENABLE 0x1b8
#define TEGRA186_ASRC_STREAM5_ENABLE 0x238
#define TEGRA186_ASRC_STREAM6_ENABLE 0x2b8
#define TEGRA186_ASRC_STREAM2_CONFIG 0x80
#define TEGRA186_ASRC_STREAM3_CONFIG 0x100
#define TEGRA186_ASRC_STREAM4_CONFIG 0x180
#define TEGRA186_ASRC_STREAM5_CONFIG 0x200
#define TEGRA186_ASRC_STREAM6_CONFIG 0x280
#define TEGRA186_ASRC_STREAM2_TX_THRESHOLD 0x94
#define TEGRA186_ASRC_STREAM3_TX_THRESHOLD 0x114
#define TEGRA186_ASRC_STREAM4_TX_THRESHOLD 0x194
#define TEGRA186_ASRC_STREAM5_TX_THRESHOLD 0x214
#define TEGRA186_ASRC_STREAM6_TX_THRESHOLD 0x294
#define TEGRA186_ASRC_STREAM2_RX_THRESHOLD 0x98
#define TEGRA186_ASRC_STREAM3_RX_THRESHOLD 0x118
#define TEGRA186_ASRC_STREAM4_RX_THRESHOLD 0x198
#define TEGRA186_ASRC_STREAM5_RX_THRESHOLD 0x218
#define TEGRA186_ASRC_STREAM6_RX_THRESHOLD 0x298
/* ASRC UPD related offset */
#define TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL 0x30c
#define TEGRA186_ASRC_RATIO_UPD_RX_STATUS 0x310
/* ASRC Global registers offset */
#define TEGRA186_ASRC_GLOBAL_ENB 0x2f4
#define TEGRA186_ASRC_GLOBAL_SOFT_RESET 0x2f8
#define TEGRA186_ASRC_GLOBAL_CG 0x2fc
#define TEGRA186_ASRC_GLOBAL_CONFIG 0x300
#define TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR 0x304
#define TEGRA186_ASRC_GLOBAL_SCRATCH_CONFIG 0x308
#define TEGRA186_ASRC_GLOBAL_STATUS 0x314
#define TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS 0x318
#define TEGRA186_ASRC_GLOBAL_INT_STATUS 0x324
#define TEGRA186_ASRC_GLOBAL_INT_MASK 0x328
#define TEGRA186_ASRC_GLOBAL_INT_SET 0x32c
#define TEGRA186_ASRC_GLOBAL_INT_CLEAR 0x330
#define TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG 0x334
#define TEGRA186_ASRC_GLOBAL_APR_CTRL 0x1000
#define TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL 0x1004
#define TEGRA186_ASRC_GLOBAL_DISARM_APR 0x1008
#define TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL 0x100c
#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS 0x1010
#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL 0x1014
#define TEGRA186_ASRC_CYA 0x1018
#define TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE 0xaaaa
#define TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CONFIG 0x00201002
#define TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CONFIG 0x00201002
#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT 31
#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE (0 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT)
#define TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT 0
#define TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK (1 << TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT)
#define TEGRA186_ASRC_STREAM_EN_SHIFT 0
#define TEGRA186_ASRC_STREAM_EN (1 << TEGRA186_ASRC_STREAM_EN_SHIFT)
#define TEGRA186_ASRC_GLOBAL_EN_SHIFT 0
#define TEGRA186_ASRC_GLOBAL_EN (1 << TEGRA186_ASRC_GLOBAL_EN_SHIFT)
#define TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_SHIFT 0
#define TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_STATEBUF_CONFIG_SIZE_SHIFT)
#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_SHIFT 0
#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_INSAMPLEBUF_CONFIG_SIZE_SHIFT)
#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_SHIFT 0
#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CONFIG_SIZE_SHIFT)
#define TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_28BIT_PRECISION 0
#define TEGRA186_ASRC_GLOBAL_CONFIG_FRAC_32BIT_PRECISION 1
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_SHIFT 24
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_MASK (0xFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_MAX_CHANNELS_SHIFT)
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_SHIFT 16
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_MASK (0xFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_BLOCK_SIZE_SHIFT)
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_SHIFT 0
#define TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_MASK (0xFFFF << TEGRA186_ASRC_STREAM_GLOBAL_SCRATCH_CONFIG_SIZE_SHIFT)
#define TEGRA186_ASRC_STREAM_RATIO_INTEGER_PART_MASK 0x1F
#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK 0xFFFFFFFF
#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MAX 0x7FFFFFFF
enum asrc_task_event {
STREAM_DISABLE,
STREAM_ENABLE,
};
struct tegra_cif_conf;
struct tegra186_asrc_lane {
unsigned int int_part;
unsigned int frac_part;
int ratio_source;
unsigned int hwcomp_disable;
unsigned int input_thresh;
unsigned int output_thresh;
};
struct tegra_asrc_soc_data {
unsigned int aram_start_addr;
};
struct tegra186_asrc {
const struct tegra_asrc_soc_data *soc_data;
struct regmap *regmap;
struct tegra186_asrc_lane lane[6];
struct tasklet_struct tasklet;
struct list_head task_desc;
int active_dai_count;
};
int tegra186_asrc_set_source(int id, int source);
int tegra186_asrc_event(int id, enum asrc_task_event event, int status);
int tegra186_asrc_update_ratio(int id, int inte, int frac);
void tegra186_asrc_handle_arad_unlock(int stream_id, int action);
#endif

View File

@@ -0,0 +1,711 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra186_dspk.c - Tegra186 DSPK driver
//
// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#include <linux/clk.h>
#include <linux/device.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_params.h>
#include <sound/soc.h>
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra186_dspk.h"
static const struct reg_default tegra186_dspk_reg_defaults[] = {
{ TEGRA186_DSPK_RX_INT_MASK, 0x00000007 },
{ TEGRA186_DSPK_RX_CIF_CTRL, 0x00007700 },
{ TEGRA186_DSPK_CG, 0x00000001 },
{ TEGRA186_DSPK_CORE_CTRL, 0x00000310 },
{ TEGRA186_DSPK_CODEC_CTRL, 0x03000000 },
};
static int tegra186_dspk_get_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.integer.value[0] = dspk->srate_override;
return 0;
}
static int tegra186_dspk_put_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
int value = ucontrol->value.integer.value[0];
if (dspk->srate_override == value)
return 0;
dspk->srate_override = value;
return 1;
}
static int tegra186_dspk_get_audio_bitfmt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->audio_fmt_override;
return 0;
}
static int tegra186_dspk_put_audio_bitfmt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (dspk->audio_fmt_override == value)
return 0;
dspk->audio_fmt_override = value;
return 1;
}
static int tegra186_dspk_get_audio_ch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.integer.value[0] = dspk->audio_ch_override;
return 0;
}
static int tegra186_dspk_put_audio_ch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
int value = ucontrol->value.integer.value[0];
if (dspk->audio_ch_override == value)
return 0;
dspk->audio_ch_override = value;
return 1;
}
static int tegra186_dspk_get_fifo_th(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.integer.value[0] = dspk->rx_fifo_th;
return 0;
}
static int tegra186_dspk_put_fifo_th(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
int value = ucontrol->value.integer.value[0];
if (value == dspk->rx_fifo_th)
return 0;
dspk->rx_fifo_th = value;
return 1;
}
static int tegra186_dspk_get_osr_val(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->osr_val;
return 0;
}
static int tegra186_dspk_put_osr_val(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dspk->osr_val)
return 0;
dspk->osr_val = value;
return 1;
}
static int tegra186_dspk_get_pol_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->lrsel;
return 0;
}
static int tegra186_dspk_put_pol_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dspk->lrsel)
return 0;
dspk->lrsel = value;
return 1;
}
static int tegra186_dspk_get_ch_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->ch_sel;
return 0;
}
static int tegra186_dspk_put_ch_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dspk->ch_sel)
return 0;
dspk->ch_sel = value;
return 1;
}
static int tegra186_dspk_get_mono_to_stereo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->mono_to_stereo;
return 0;
}
static int tegra186_dspk_put_mono_to_stereo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dspk->mono_to_stereo)
return 0;
dspk->mono_to_stereo = value;
return 1;
}
static int tegra186_dspk_get_stereo_to_mono(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = dspk->stereo_to_mono;
return 0;
}
static int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dspk->stereo_to_mono)
return 0;
dspk->stereo_to_mono = value;
return 1;
}
static int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev)
{
struct tegra186_dspk *dspk = dev_get_drvdata(dev);
regcache_cache_only(dspk->regmap, true);
regcache_mark_dirty(dspk->regmap);
clk_disable_unprepare(dspk->clk_dspk);
return 0;
}
static int __maybe_unused tegra186_dspk_runtime_resume(struct device *dev)
{
struct tegra186_dspk *dspk = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(dspk->clk_dspk);
if (err) {
dev_err(dev, "failed to enable DSPK clock, err: %d\n", err);
return err;
}
regcache_cache_only(dspk->regmap, false);
regcache_sync(dspk->regmap);
return 0;
}
static const unsigned int tegra186_dspk_fmts[] = {
0,
TEGRA_ACIF_BITS_16,
TEGRA_ACIF_BITS_32,
};
static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai);
unsigned int channels, srate, dspk_clk;
struct device *dev = dai->dev;
struct tegra_cif_conf cif_conf;
unsigned int max_th;
int err;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
cif_conf.audio_ch = channels;
/* Override audio channel */
if (dspk->audio_ch_override)
cif_conf.audio_ch = dspk->audio_ch_override;
/* Client channel */
switch (dspk->ch_sel) {
case DSPK_CH_SELECT_LEFT:
case DSPK_CH_SELECT_RIGHT:
cif_conf.client_ch = 1;
break;
case DSPK_CH_SELECT_STEREO:
cif_conf.client_ch = 2;
break;
default:
dev_err(dev, "Invalid DSPK client channels\n");
return -EINVAL;
}
cif_conf.client_bits = TEGRA_ACIF_BITS_24;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
cif_conf.client_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
dev_err(dev, "unsupported format!\n");
return -ENOTSUPP;
}
/* Audio bit format override */
if (dspk->audio_fmt_override)
cif_conf.audio_bits =
tegra186_dspk_fmts[dspk->audio_fmt_override];
srate = params_rate(params);
/* Sample rate override */
if (dspk->srate_override)
srate = dspk->srate_override;
/* RX FIFO threshold in terms of frames */
max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
if (dspk->rx_fifo_th > max_th)
dspk->rx_fifo_th = max_th;
cif_conf.threshold = dspk->rx_fifo_th;
cif_conf.mono_conv = dspk->mono_to_stereo;
cif_conf.stereo_conv = dspk->stereo_to_mono;
tegra_set_cif(dspk->regmap, TEGRA186_DSPK_RX_CIF_CTRL,
&cif_conf);
/*
* DSPK clock and PDM codec clock should be synchronous with 4:1 ratio,
* this is because it takes 4 clock cycles to send out one sample to
* codec by sigma delta modulator. Finally the clock rate is a multiple
* of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'.
*/
dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO;
err = clk_set_rate(dspk->clk_dspk, dspk_clk);
if (err) {
dev_err(dev, "can't set DSPK clock rate %u, err: %d\n",
dspk_clk, err);
return err;
}
regmap_update_bits(dspk->regmap,
/* Reg */
TEGRA186_DSPK_CORE_CTRL,
/* Mask */
TEGRA186_DSPK_OSR_MASK |
TEGRA186_DSPK_CHANNEL_SELECT_MASK |
TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK,
/* Value */
(dspk->osr_val << DSPK_OSR_SHIFT) |
((dspk->ch_sel + 1) << CH_SEL_SHIFT) |
(dspk->lrsel << LRSEL_POL_SHIFT));
return 0;
}
static const struct snd_soc_dai_ops tegra186_dspk_dai_ops = {
.hw_params = tegra186_dspk_hw_params,
};
/*
* Three DAIs are exposed
* 1. "CIF" DAI for connecting with XBAR
* 2. "DAP" DAI for connecting with CODEC
* 3. "DUMMY_SINK" can be used when no external
* codec connection is available. In such case
* "DAP" is connected with "DUMMY_SINK"
* Order of these DAIs should not be changed, since DAI links in DT refer
* to these DAIs depending on the index.
*/
static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
{
.name = "DSPK-CIF",
.playback = {
.stream_name = "CIF-Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "DSPK-DAP",
#if IS_ENABLED(CONFIG_TEGRA_DPCM)
.playback = {
.stream_name = "DAP-Playback",
#else
.capture = {
.stream_name = "DAP-Capture",
#endif
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &tegra186_dspk_dai_ops,
.symmetric_rate = 1,
},
{
.name = "DUMMY_SINK",
.playback = {
.stream_name = "Dummy-Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
};
static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = {
SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
SND_SOC_DAPM_SPK("SPK", NULL),
};
static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {
#if IS_ENABLED(CONFIG_TEGRA_DPCM)
{ "XBAR-Playback", NULL, "XBAR-TX" },
{ "CIF-Playback", NULL, "XBAR-Playback" },
{ "RX", NULL, "CIF-Playback" },
{ "DAP-Playback", NULL, "RX" },
{ "SPK", NULL, "DAP-Playback" },
#else
{ "RX", NULL, "CIF-Playback" },
{ "DAP-Capture", NULL, "RX" },
{ "SPK", NULL, "Dummy-Playback" },
#endif
};
static const char * const tegra186_dspk_format_text[] = {
"None",
"16",
"32",
};
static const struct soc_enum tegra186_dspk_format_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_format_text),
tegra186_dspk_format_text);
static const char * const tegra186_dspk_ch_sel_text[] = {
"Left", "Right", "Stereo",
};
static const struct soc_enum tegra186_dspk_ch_sel_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text),
tegra186_dspk_ch_sel_text);
static const char * const tegra186_dspk_osr_text[] = {
"OSR_32", "OSR_64", "OSR_128", "OSR_256",
};
static const struct soc_enum tegra186_dspk_osr_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text),
tegra186_dspk_osr_text);
static const char * const tegra186_dspk_lrsel_text[] = {
"Left", "Right",
};
static const char * const tegra186_dspk_mono_conv_text[] = {
"Zero", "Copy",
};
static const struct soc_enum tegra186_dspk_mono_conv_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
ARRAY_SIZE(tegra186_dspk_mono_conv_text),
tegra186_dspk_mono_conv_text);
static const char * const tegra186_dspk_stereo_conv_text[] = {
"CH0", "CH1", "AVG",
};
static const struct soc_enum tegra186_dspk_stereo_conv_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
ARRAY_SIZE(tegra186_dspk_stereo_conv_text),
tegra186_dspk_stereo_conv_text);
static const struct soc_enum tegra186_dspk_lrsel_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text),
tegra186_dspk_lrsel_text);
static const struct snd_kcontrol_new tegrat186_dspk_controls[] = {
SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0,
TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0,
tegra186_dspk_get_fifo_th, tegra186_dspk_put_fifo_th),
SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum,
tegra186_dspk_get_osr_val, tegra186_dspk_put_osr_val),
SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,
tegra186_dspk_get_pol_sel, tegra186_dspk_put_pol_sel),
SOC_SINGLE_EXT("Sample Rate", SND_SOC_NOPM, 0, 48000, 0,
tegra186_dspk_get_sample_rate,
tegra186_dspk_put_sample_rate),
SOC_SINGLE_EXT("Audio Channels", SND_SOC_NOPM, 0, 2, 0,
tegra186_dspk_get_audio_ch,
tegra186_dspk_put_audio_ch),
SOC_ENUM_EXT("Audio Bit Format", tegra186_dspk_format_enum,
tegra186_dspk_get_audio_bitfmt,
tegra186_dspk_put_audio_bitfmt),
SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,
tegra186_dspk_get_ch_sel, tegra186_dspk_put_ch_sel),
SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,
tegra186_dspk_get_mono_to_stereo,
tegra186_dspk_put_mono_to_stereo),
SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum,
tegra186_dspk_get_stereo_to_mono,
tegra186_dspk_put_stereo_to_mono),
};
static const struct snd_soc_component_driver tegra186_dspk_cmpnt = {
.dapm_widgets = tegra186_dspk_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets),
.dapm_routes = tegra186_dspk_routes,
.num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes),
.controls = tegrat186_dspk_controls,
.num_controls = ARRAY_SIZE(tegrat186_dspk_controls),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA186_DSPK_RX_INT_MASK ... TEGRA186_DSPK_RX_CIF_CTRL:
case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG:
case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL:
return true;
default:
return false;
};
}
static bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg)
{
if (tegra186_dspk_wr_reg(dev, reg))
return true;
switch (reg) {
case TEGRA186_DSPK_RX_STATUS:
case TEGRA186_DSPK_RX_INT_STATUS:
case TEGRA186_DSPK_STATUS:
case TEGRA186_DSPK_INT_STATUS:
return true;
default:
return false;
};
}
static bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA186_DSPK_RX_STATUS:
case TEGRA186_DSPK_RX_INT_STATUS:
case TEGRA186_DSPK_STATUS:
case TEGRA186_DSPK_INT_STATUS:
return true;
default:
return false;
};
}
static const struct regmap_config tegra186_dspk_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA186_DSPK_CODEC_CTRL,
.writeable_reg = tegra186_dspk_wr_reg,
.readable_reg = tegra186_dspk_rd_reg,
.volatile_reg = tegra186_dspk_volatile_reg,
.reg_defaults = tegra186_dspk_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra186_dspk_of_match[] = {
{ .compatible = "nvidia,tegra186-dspk" },
{},
};
MODULE_DEVICE_TABLE(of, tegra186_dspk_of_match);
static int tegra186_dspk_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra186_dspk *dspk;
void __iomem *regs;
int err;
dspk = devm_kzalloc(dev, sizeof(*dspk), GFP_KERNEL);
if (!dspk)
return -ENOMEM;
dspk->osr_val = DSPK_OSR_64;
dspk->lrsel = DSPK_LRSEL_LEFT;
dspk->ch_sel = DSPK_CH_SELECT_STEREO;
dspk->mono_to_stereo = 0; /* "Zero" */
dev_set_drvdata(dev, dspk);
dspk->clk_dspk = devm_clk_get(dev, "dspk");
if (IS_ERR(dspk->clk_dspk)) {
dev_err(dev, "can't retrieve DSPK clock\n");
return PTR_ERR(dspk->clk_dspk);
}
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap);
if (IS_ERR(dspk->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(dspk->regmap);
}
regcache_cache_only(dspk->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt,
tegra186_dspk_dais,
ARRAY_SIZE(tegra186_dspk_dais));
if (err) {
dev_err(dev, "can't register DSPK component, err: %d\n",
err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra186_dspk_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra186_dspk_pm_ops = {
SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend,
tegra186_dspk_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra186_dspk_driver = {
.driver = {
.name = "tegra186-dspk",
.of_match_table = tegra186_dspk_of_match,
.pm = &tegra186_dspk_pm_ops,
},
.probe = tegra186_dspk_platform_probe,
.remove = tegra186_dspk_platform_remove,
};
module_platform_driver(tegra186_dspk_driver);
MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
MODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>");
MODULE_DESCRIPTION("Tegra186 ASoC DSPK driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra186_dspk.h - Definitions for Tegra186 DSPK driver
*
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA186_DSPK_H__
#define __TEGRA186_DSPK_H__
/* Register offsets from DSPK BASE */
#define TEGRA186_DSPK_RX_STATUS 0x0c
#define TEGRA186_DSPK_RX_INT_STATUS 0x10
#define TEGRA186_DSPK_RX_INT_MASK 0x14
#define TEGRA186_DSPK_RX_INT_SET 0x18
#define TEGRA186_DSPK_RX_INT_CLEAR 0x1c
#define TEGRA186_DSPK_RX_CIF_CTRL 0x20
#define TEGRA186_DSPK_ENABLE 0x40
#define TEGRA186_DSPK_SOFT_RESET 0x44
#define TEGRA186_DSPK_CG 0x48
#define TEGRA186_DSPK_STATUS 0x4c
#define TEGRA186_DSPK_INT_STATUS 0x50
#define TEGRA186_DSPK_CORE_CTRL 0x60
#define TEGRA186_DSPK_CODEC_CTRL 0x64
/* DSPK CORE CONTROL fields */
#define CH_SEL_SHIFT 8
#define TEGRA186_DSPK_CHANNEL_SELECT_MASK (0x3 << CH_SEL_SHIFT)
#define DSPK_OSR_SHIFT 4
#define TEGRA186_DSPK_OSR_MASK (0x3 << DSPK_OSR_SHIFT)
#define LRSEL_POL_SHIFT 0
#define TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK (0x1 << LRSEL_POL_SHIFT)
#define TEGRA186_DSPK_RX_FIFO_DEPTH 64
#define DSPK_OSR_FACTOR 32
/* DSPK interface clock ratio */
#define DSPK_CLK_RATIO 4
enum tegra_dspk_osr {
DSPK_OSR_32,
DSPK_OSR_64,
DSPK_OSR_128,
DSPK_OSR_256,
};
enum tegra_dspk_ch_sel {
DSPK_CH_SELECT_LEFT,
DSPK_CH_SELECT_RIGHT,
DSPK_CH_SELECT_STEREO,
};
enum tegra_dspk_lrsel {
DSPK_LRSEL_LEFT,
DSPK_LRSEL_RIGHT,
};
struct tegra186_dspk {
unsigned int rx_fifo_th;
unsigned int osr_val;
unsigned int lrsel;
unsigned int srate_override;
unsigned int audio_ch_override;
unsigned int audio_fmt_override;
unsigned int ch_sel;
unsigned int mono_to_stereo;
unsigned int stereo_to_mono;
struct clk *clk_dspk;
struct regmap *regmap;
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_admaif.h - Tegra ADMAIF registers
*
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA_ADMAIF_H__
#define __TEGRA_ADMAIF_H__
#define TEGRA_ADMAIF_CHANNEL_REG_STRIDE 0x40
/* Tegra210 specific */
#define TEGRA210_ADMAIF_LAST_REG 0x75f
#define TEGRA210_ADMAIF_CHANNEL_COUNT 10
#define TEGRA210_ADMAIF_RX_BASE 0x0
#define TEGRA210_ADMAIF_TX_BASE 0x300
#define TEGRA210_ADMAIF_GLOBAL_BASE 0x700
/* Tegra186 specific */
#define TEGRA186_ADMAIF_LAST_REG 0xd5f
#define TEGRA186_ADMAIF_CHANNEL_COUNT 20
#define TEGRA186_ADMAIF_RX_BASE 0x0
#define TEGRA186_ADMAIF_TX_BASE 0x500
#define TEGRA186_ADMAIF_GLOBAL_BASE 0xd00
/* Global registers */
#define TEGRA_ADMAIF_GLOBAL_ENABLE 0x0
#define TEGRA_ADMAIF_GLOBAL_CG_0 0x8
#define TEGRA_ADMAIF_GLOBAL_STATUS 0x10
#define TEGRA_ADMAIF_GLOBAL_RX_ENABLE_STATUS 0x20
#define TEGRA_ADMAIF_GLOBAL_TX_ENABLE_STATUS 0x24
/* RX channel registers */
#define TEGRA_ADMAIF_RX_ENABLE 0x0
#define TEGRA_ADMAIF_RX_SOFT_RESET 0x4
#define TEGRA_ADMAIF_RX_STATUS 0xc
#define TEGRA_ADMAIF_RX_INT_STATUS 0x10
#define TEGRA_ADMAIF_RX_INT_MASK 0x14
#define TEGRA_ADMAIF_RX_INT_SET 0x18
#define TEGRA_ADMAIF_RX_INT_CLEAR 0x1c
#define TEGRA_ADMAIF_CH_ACIF_RX_CTRL 0x20
#define TEGRA_ADMAIF_RX_FIFO_CTRL 0x28
#define TEGRA_ADMAIF_RX_FIFO_READ 0x2c
/* TX channel registers */
#define TEGRA_ADMAIF_TX_ENABLE 0x0
#define TEGRA_ADMAIF_TX_SOFT_RESET 0x4
#define TEGRA_ADMAIF_TX_STATUS 0xc
#define TEGRA_ADMAIF_TX_INT_STATUS 0x10
#define TEGRA_ADMAIF_TX_INT_MASK 0x14
#define TEGRA_ADMAIF_TX_INT_SET 0x18
#define TEGRA_ADMAIF_TX_INT_CLEAR 0x1c
#define TEGRA_ADMAIF_CH_ACIF_TX_CTRL 0x20
#define TEGRA_ADMAIF_TX_FIFO_CTRL 0x28
#define TEGRA_ADMAIF_TX_FIFO_WRITE 0x2c
/* Bit fields */
#define PACK8_EN_SHIFT 31
#define PACK8_EN_MASK BIT(PACK8_EN_SHIFT)
#define PACK8_EN BIT(PACK8_EN_SHIFT)
#define PACK16_EN_SHIFT 30
#define PACK16_EN_MASK BIT(PACK16_EN_SHIFT)
#define PACK16_EN BIT(PACK16_EN_SHIFT)
#define TX_ENABLE_SHIFT 0
#define TX_ENABLE_MASK BIT(TX_ENABLE_SHIFT)
#define TX_ENABLE BIT(TX_ENABLE_SHIFT)
#define RX_ENABLE_SHIFT 0
#define RX_ENABLE_MASK BIT(RX_ENABLE_SHIFT)
#define RX_ENABLE BIT(RX_ENABLE_SHIFT)
#define SW_RESET_MASK 1
#define SW_RESET 1
/* Default values - Tegra210 */
#define TEGRA210_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300
#define TEGRA210_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304
#define TEGRA210_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000208
#define TEGRA210_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT 0x0000020b
#define TEGRA210_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT 0x0000020e
#define TEGRA210_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT 0x00000211
#define TEGRA210_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT 0x00000214
#define TEGRA210_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT 0x00000217
#define TEGRA210_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT 0x0000021a
#define TEGRA210_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT 0x0000021d
#define TEGRA210_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT 0x02000300
#define TEGRA210_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT 0x02000304
#define TEGRA210_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT 0x01800208
#define TEGRA210_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT 0x0180020b
#define TEGRA210_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT 0x0180020e
#define TEGRA210_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT 0x01800211
#define TEGRA210_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT 0x01800214
#define TEGRA210_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT 0x01800217
#define TEGRA210_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x0180021a
#define TEGRA210_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021d
/* Default values - Tegra186 */
#define TEGRA186_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300
#define TEGRA186_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304
#define TEGRA186_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000308
#define TEGRA186_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT 0x0000030c
#define TEGRA186_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT 0x00000210
#define TEGRA186_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT 0x00000213
#define TEGRA186_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT 0x00000216
#define TEGRA186_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT 0x00000219
#define TEGRA186_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT 0x0000021c
#define TEGRA186_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT 0x0000021f
#define TEGRA186_ADMAIF_RX11_FIFO_CTRL_REG_DEFAULT 0x00000222
#define TEGRA186_ADMAIF_RX12_FIFO_CTRL_REG_DEFAULT 0x00000225
#define TEGRA186_ADMAIF_RX13_FIFO_CTRL_REG_DEFAULT 0x00000228
#define TEGRA186_ADMAIF_RX14_FIFO_CTRL_REG_DEFAULT 0x0000022b
#define TEGRA186_ADMAIF_RX15_FIFO_CTRL_REG_DEFAULT 0x0000022e
#define TEGRA186_ADMAIF_RX16_FIFO_CTRL_REG_DEFAULT 0x00000231
#define TEGRA186_ADMAIF_RX17_FIFO_CTRL_REG_DEFAULT 0x00000234
#define TEGRA186_ADMAIF_RX18_FIFO_CTRL_REG_DEFAULT 0x00000237
#define TEGRA186_ADMAIF_RX19_FIFO_CTRL_REG_DEFAULT 0x0000023a
#define TEGRA186_ADMAIF_RX20_FIFO_CTRL_REG_DEFAULT 0x0000023d
#define TEGRA186_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT 0x02000300
#define TEGRA186_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT 0x02000304
#define TEGRA186_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT 0x02000308
#define TEGRA186_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT 0x0200030c
#define TEGRA186_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT 0x01800210
#define TEGRA186_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT 0x01800213
#define TEGRA186_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT 0x01800216
#define TEGRA186_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT 0x01800219
#define TEGRA186_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x0180021c
#define TEGRA186_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021f
#define TEGRA186_ADMAIF_TX11_FIFO_CTRL_REG_DEFAULT 0x01800222
#define TEGRA186_ADMAIF_TX12_FIFO_CTRL_REG_DEFAULT 0x01800225
#define TEGRA186_ADMAIF_TX13_FIFO_CTRL_REG_DEFAULT 0x01800228
#define TEGRA186_ADMAIF_TX14_FIFO_CTRL_REG_DEFAULT 0x0180022b
#define TEGRA186_ADMAIF_TX15_FIFO_CTRL_REG_DEFAULT 0x0180022e
#define TEGRA186_ADMAIF_TX16_FIFO_CTRL_REG_DEFAULT 0x01800231
#define TEGRA186_ADMAIF_TX17_FIFO_CTRL_REG_DEFAULT 0x01800234
#define TEGRA186_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT 0x01800237
#define TEGRA186_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT 0x0180023a
#define TEGRA186_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT 0x0180023d
enum {
DATA_8BIT,
DATA_16BIT,
DATA_32BIT
};
enum {
ADMAIF_RX_PATH,
ADMAIF_TX_PATH,
ADMAIF_PATHS,
};
struct tegra_admaif_soc_data {
const struct snd_soc_component_driver *cmpnt;
const struct regmap_config *regmap_conf;
struct snd_soc_dai_driver *dais;
unsigned int global_base;
unsigned int tx_base;
unsigned int rx_base;
unsigned int num_ch;
};
struct tegra_admaif {
struct snd_dmaengine_dai_dma_data *capture_dma_data;
struct snd_dmaengine_dai_dma_data *playback_dma_data;
const struct tegra_admaif_soc_data *soc_data;
unsigned int *audio_ch_override[ADMAIF_PATHS];
unsigned int *client_ch_override[ADMAIF_PATHS];
int reg_dump_flag;
void __iomem *base_addr;
unsigned int *mono_to_stereo[ADMAIF_PATHS];
unsigned int *stereo_to_mono[ADMAIF_PATHS];
struct regmap *regmap;
};
extern void tegra_adma_dump_ch_reg(void);
#endif

View File

@@ -0,0 +1,776 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_adx.c - Tegra210 ADX driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#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 <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_adx.h"
#include "tegra210_ahub.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},
};
/**
* tegra210_adx_enable_outstream - enable output stream
* @adx: struct of tegra210_adx
* @stream_id: adx output stream id for enabling
*/
static void tegra210_adx_enable_outstream(struct tegra210_adx *adx,
unsigned int stream_id)
{
int reg;
reg = TEGRA210_ADX_CTRL;
regmap_update_bits(adx->regmap, reg,
TEGRA210_ADX_TX_ENABLE << stream_id,
TEGRA210_ADX_TX_ENABLE << stream_id);
}
/**
* tegra210_adx_disable_outstream - disable output stream
* @adx: struct of tegra210_adx
* @stream_id: adx output stream id for disabling
*/
static void tegra210_adx_disable_outstream(struct tegra210_adx *adx,
unsigned int stream_id)
{
int reg;
reg = TEGRA210_ADX_CTRL;
regmap_update_bits(adx->regmap, reg,
TEGRA210_ADX_TX_ENABLE << stream_id,
TEGRA210_ADX_TX_DISABLE);
}
/**
* tegra210_adx_set_in_byte_mask - set byte mask for input frame
* @adx: struct of tegra210_adx
* @mask1: enable for bytes 31 ~ 0 of input frame
* @mask2: enable for bytes 63 ~ 32 of input frame
*/
static void tegra210_adx_set_in_byte_mask(struct tegra210_adx *adx)
{
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]);
}
/**
* tegra210_adx_set_map_table - set map table not RAM
* @adx: struct of tegra210_adx
* @out_byte_addr: byte address in one frame
* @stream_id: input stream id
* @nth_word: n-th word in the input stream
* @nth_byte: n-th byte in the word
*/
static void tegra210_adx_set_map_table(struct tegra210_adx *adx,
unsigned int out_byte_addr,
unsigned int stream_id,
unsigned int nth_word,
unsigned int nth_byte)
{
unsigned char *bytes_map = (unsigned char *)&adx->map;
bytes_map[out_byte_addr] =
(stream_id << TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT) |
(nth_word << TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT) |
(nth_byte << TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT);
}
/**
* tegra210_adx_write_map_ram - write map information in RAM
* @adx: struct of tegra210_adx
* @addr: n-th word of input stream
* @val : bytes mapping information of the word
*/
static void tegra210_adx_write_map_ram(struct tegra210_adx *adx,
unsigned int addr,
unsigned int val)
{
unsigned int reg;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
(addr << TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT));
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, val);
regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &reg);
reg |= TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, reg);
regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &reg);
reg |= TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, reg);
}
static void tegra210_adx_update_map_ram(struct tegra210_adx *adx)
{
int i;
for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
tegra210_adx_write_map_ram(adx, i, adx->map[i]);
}
static int tegra210_adx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct device *dev = cmpnt->dev;
struct tegra210_adx *adx = dev_get_drvdata(dev);
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(dev, "failed to stop ADX, err = %d\n", err);
return err;
}
/* SW reset */
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(dev, "failed to reset ADX, err = %d\n", err);
return err;
}
regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT);
return 0;
}
static unsigned int __maybe_unused
tegra210_adx_read_map_ram(struct tegra210_adx *adx, unsigned int addr)
{
unsigned int val;
int err;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
(addr << TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT));
regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &val);
val |= TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN;
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, val);
regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, &val);
val &= ~(TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);
regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, val);
err = regmap_read_poll_timeout(adx->regmap,
TEGRA210_ADX_CFG_RAM_CTRL,
val, !(val & 0x80000000), 10, 10000);
if (err < 0)
return err;
regmap_read(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, &val);
return val;
}
static int 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 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);
/* update the map ram */
tegra210_adx_update_map_ram(adx);
tegra210_adx_set_in_byte_mask(adx);
return 0;
}
static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
int channels, 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_S24_LE:
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)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
int channels;
if (adx->output_channels[dai->id] > 0)
channels = adx->output_channels[dai->id];
else
channels = params_channels(params);
return tegra210_adx_set_audio_cif(dai, channels, params_format(params),
TEGRA210_ADX_TX1_CIF_CTRL +
(dai->id * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));
}
static int tegra210_adx_out_trigger(struct snd_pcm_substream *substream,
int cmd,
struct snd_soc_dai *dai)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
tegra210_adx_enable_outstream(adx, dai->id);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
tegra210_adx_disable_outstream(adx, dai->id);
break;
default:
return -EINVAL;
}
return 0;
}
static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
int channels;
if (adx->input_channels > 0)
channels = adx->input_channels;
else
channels = params_channels(params);
return tegra210_adx_set_audio_cif(dai, channels, params_format(params),
TEGRA210_ADX_RX_CIF_CTRL);
}
static int tegra210_adx_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct device *dev = dai->dev;
struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
unsigned int out_stream_idx, out_ch_idx, out_byte_idx;
int i;
if ((rx_num < 1) || (rx_num > 64)) {
dev_err(dev, "Doesn't support %d rx_num, need to be 1 to 64\n",
rx_num);
return -EINVAL;
}
if (!rx_slot) {
dev_err(dev, "rx_slot is NULL\n");
return -EINVAL;
}
memset(adx->map, 0, sizeof(adx->map));
memset(adx->byte_mask, 0, sizeof(adx->byte_mask));
for (i = 0; i < rx_num; i++) {
if (rx_slot[i] != 0) {
/* getting mapping information */
/* n-th output stream : 0 to 3 */
out_stream_idx = (rx_slot[i] >> 16) & 0x3;
/* n-th audio channel of output stream : 1 to 16 */
out_ch_idx = (rx_slot[i] >> 8) & 0x1f;
/* n-th byte of audio channel : 0 to 3 */
out_byte_idx = rx_slot[i] & 0x3;
tegra210_adx_set_map_table(adx, i, out_stream_idx,
out_ch_idx - 1,
out_byte_idx);
/* making byte_mask */
if (i > 31)
adx->byte_mask[1] |= (1 << (i - 32));
else
adx->byte_mask[0] |= (1 << i);
}
}
return 0;
}
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] = 256;
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);
struct soc_mixer_control *mc;
unsigned char *bytes_map = (unsigned char *)&adx->map;
int value = ucontrol->value.integer.value[0];
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 0;
}
static int tegra210_adx_get_in_channels(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);
ucontrol->value.integer.value[0] = adx->input_channels;
return 0;
}
static int tegra210_adx_put_in_channels(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);
int value = ucontrol->value.integer.value[0];
if (value < 0 || value > 16)
return -EINVAL;
adx->input_channels = value;
return 0;
}
static int tegra210_adx_get_out_channels(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;
mc = (struct soc_mixer_control *)kcontrol->private_value;
ucontrol->value.integer.value[0] = adx->output_channels[mc->reg - 1];
return 0;
}
static int tegra210_adx_put_out_channels(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;
int value = ucontrol->value.integer.value[0];
mc = (struct soc_mixer_control *)kcontrol->private_value;
if (value < 0 || value > 16)
return -EINVAL;
adx->output_channels[mc->reg - 1] = value;
return 0;
}
static struct snd_soc_dai_ops tegra210_adx_in_dai_ops = {
.hw_params = tegra210_adx_in_hw_params,
.set_channel_map = tegra210_adx_set_channel_map,
};
static struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {
.hw_params = tegra210_adx_out_hw_params,
.trigger = tegra210_adx_out_trigger,
};
#define OUT_DAI(id) \
{ \
.name = "OUT" #id, \
.capture = { \
.stream_name = "OUT" #id " Transmit", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.ops = &tegra210_adx_out_dai_ops, \
}
#define IN_DAI(sname, dai_ops) \
{ \
.name = #sname, \
.playback = { \
.stream_name = #sname " Receive", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.ops = dai_ops, \
}
static struct snd_soc_dai_driver tegra210_adx_dais[] = {
OUT_DAI(1),
OUT_DAI(2),
OUT_DAI(3),
OUT_DAI(4),
IN_DAI(IN, &tegra210_adx_in_dai_ops),
};
static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
SND_SOC_DAPM_AIF_IN_E("IN", NULL, 0, TEGRA210_ADX_ENABLE,
TEGRA210_ADX_ENABLE_SHIFT, 0,
tegra210_adx_stop, SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
SND_SOC_DAPM_AIF_OUT("OUT4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),
};
static const struct snd_soc_dapm_route tegra210_adx_routes[] = {
{ "IN", NULL, "IN Receive" },
{ "OUT1", NULL, "IN" },
{ "OUT2", NULL, "IN" },
{ "OUT3", NULL, "IN" },
{ "OUT4", NULL, "IN" },
{ "OUT1 Transmit", NULL, "OUT1" },
{ "OUT2 Transmit", NULL, "OUT2" },
{ "OUT3 Transmit", NULL, "OUT3" },
{ "OUT4 Transmit", NULL, "OUT4" },
};
#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)
#define TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(reg) \
SOC_SINGLE_EXT("Output" #reg " Audio Channels", reg, 0, 16, 0, \
tegra210_adx_get_out_channels, \
tegra210_adx_put_out_channels)
#define TEGRA210_ADX_INPUT_CHANNELS_CTRL(reg) \
SOC_SINGLE_EXT("Input Audio Channels", reg, 0, 16, 0, \
tegra210_adx_get_in_channels, \
tegra210_adx_put_in_channels)
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),
TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(1),
TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(2),
TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(3),
TEGRA210_ADX_OUTPUT_CHANNELS_CTRL(4),
TEGRA210_ADX_INPUT_CHANNELS_CTRL(1),
};
static 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),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
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_CYA:
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,96 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_adx.h - Definitions for Tegra210 ADX driver
*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_ADX_H__
#define __TEGRA210_ADX_H__
#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4
/* 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_CYA 0xb0
#define TEGRA210_ADX_DBG 0xb4
#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_RW_SHIFT 14
#define TEGRA210_ADX_CFG_RAM_CTRL_RW_MASK (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0
#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_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)
/*
* These defines are not in register field.
*/
#define TEGRA210_ADX_NUM_OUTPUTS 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
enum {
TEGRA210_ADX_TX_DISABLE,
TEGRA210_ADX_TX_ENABLE,
};
enum {
/* Code assumes that OUT_STREAM values of ADX start at 0 */
/* OUT_STREAM# is equilvant to hw OUT_CH# */
TEGRA210_ADX_OUT_STREAM0 = 0,
TEGRA210_ADX_OUT_STREAM1,
TEGRA210_ADX_OUT_STREAM2,
TEGRA210_ADX_OUT_STREAM3,
TEGRA210_ADX_IN_STREAM,
TEGRA210_ADX_TOTAL_STREAM
};
struct tegra210_adx {
struct regmap *regmap;
unsigned int map[TEGRA210_ADX_RAM_DEPTH];
unsigned int byte_mask[2];
int input_channels;
int output_channels[TEGRA210_ADX_NUM_OUTPUTS];
};
#endif

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2014-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// //
// tegra210_afc.c - Tegra210 AFC driver // tegra210_afc.c - Tegra210 AFC driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h> #include <nvidia/conftest.h>
@@ -22,6 +23,7 @@
#include <drivers-private/sound/soc/tegra/tegra_cif.h> #include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_afc.h" #include "tegra210_afc.h"
#include "tegra210_ahub.h"
static const struct reg_default tegra210_afc_reg_defaults[] = { static const struct reg_default tegra210_afc_reg_defaults[] = {
{ TEGRA210_AFC_AXBAR_RX_CIF_CTRL, 0x00007700}, { TEGRA210_AFC_AXBAR_RX_CIF_CTRL, 0x00007700},

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_ahub.h - TEGRA210 AHUB
*
* Copyright (c) 2020-2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_AHUB__H__
#define __TEGRA210_AHUB__H__
/* Tegra210 specific */
#define TEGRA210_XBAR_PART1_RX 0x200
#define TEGRA210_XBAR_PART2_RX 0x400
#define TEGRA210_XBAR_RX_STRIDE 0x4
#define TEGRA210_XBAR_AUDIO_RX_COUNT 90
#define TEGRA210_XBAR_REG_MASK_0 0xf1f03ff
#define TEGRA210_XBAR_REG_MASK_1 0x3f30031f
#define TEGRA210_XBAR_REG_MASK_2 0xff1cf313
#define TEGRA210_XBAR_REG_MASK_3 0x0
#define TEGRA210_XBAR_UPDATE_MAX_REG 3
/* Tegra186 specific */
#define TEGRA186_XBAR_PART3_RX 0x600
#define TEGRA186_XBAR_AUDIO_RX_COUNT 115
#define TEGRA186_XBAR_REG_MASK_0 0xf3fffff
#define TEGRA186_XBAR_REG_MASK_1 0x3f310f1f
#define TEGRA186_XBAR_REG_MASK_2 0xff3cf311
#define TEGRA186_XBAR_REG_MASK_3 0x3f0f00ff
#define TEGRA186_XBAR_UPDATE_MAX_REG 4
/* Fields in *AHUBRAMCTL_CTRL; used by different AHUB modules */
#define TEGRA210_AHUBRAMCTL_CTRL_RW_READ 0
#define TEGRA210_AHUBRAMCTL_CTRL_RW_WRITE (1 << 14)
#define TEGRA210_AHUBRAMCTL_CTRL_ADDR_INIT_EN (1 << 13)
#define TEGRA210_AHUBRAMCTL_CTRL_SEQ_ACCESS_EN (1 << 12)
#define TEGRA210_AHUBRAMCTL_CTRL_RAM_ADDR_MASK 0x1ff
#define TEGRA_XBAR_UPDATE_MAX_REG (TEGRA186_XBAR_UPDATE_MAX_REG)
#define TEGRA186_MAX_REGISTER_ADDR (TEGRA186_XBAR_PART3_RX + \
(TEGRA210_XBAR_RX_STRIDE * (TEGRA186_XBAR_AUDIO_RX_COUNT - 1)))
#define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX + \
(TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1)))
#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id))
#define MUX_VALUE(npart, nbit) (1 + (nbit) + (npart) * 32)
#define SOC_VALUE_ENUM_WIDE(xreg, shift, xmax, xtexts, xvalues) \
{ \
.reg = xreg, \
.shift_l = shift, \
.shift_r = shift, \
.items = xmax, \
.texts = xtexts, \
.values = xvalues, \
.mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0 \
}
#define SOC_VALUE_ENUM_WIDE_DECL(name, xreg, shift, xtexts, xvalues) \
static struct soc_enum name = \
SOC_VALUE_ENUM_WIDE(xreg, shift, ARRAY_SIZE(xtexts), \
xtexts, xvalues)
#define MUX_ENUM_CTRL_DECL(ename, id) \
SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \
tegra210_ahub_mux_texts, \
tegra210_ahub_mux_values); \
static const struct snd_kcontrol_new ename##_control = \
SOC_DAPM_ENUM_EXT("Route", ename##_enum, \
tegra_ahub_get_value_enum, \
tegra_ahub_put_value_enum)
#define MUX_ENUM_CTRL_DECL_186(ename, id) \
SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \
tegra186_ahub_mux_texts, \
tegra186_ahub_mux_values); \
static const struct snd_kcontrol_new ename##_control = \
SOC_DAPM_ENUM_EXT("Route", ename##_enum, \
tegra_ahub_get_value_enum, \
tegra_ahub_put_value_enum)
#define IN_OUT_ROUTES(name) \
{ name " XBAR-RX", NULL, name " XBAR-Playback" }, \
{ name " XBAR-Capture", NULL, name " XBAR-TX" },
#define WIDGETS(sname, ename) \
SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \
SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0), \
SND_SOC_DAPM_MUX(sname " Mux", SND_SOC_NOPM, 0, 0, \
&ename##_control)
#define TX_WIDGETS(sname) \
SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \
SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0)
#define DAI(sname) \
{ \
.name = "XBAR-" #sname, \
.playback = { \
.stream_name = #sname " XBAR-Playback", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_KNOT, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.capture = { \
.stream_name = #sname " XBAR-Capture", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_KNOT, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
}
struct tegra_ahub_soc_data {
const struct regmap_config *regmap_config;
const struct snd_soc_component_driver *cmpnt_drv;
struct snd_soc_dai_driver *dai_drv;
unsigned int mask[4];
unsigned int reg_count;
unsigned int num_dais;
};
struct tegra_ahub {
const struct tegra_ahub_soc_data *soc_data;
struct regmap *regmap;
struct clk *clk;
};
void tegra210_ahub_write_ram(struct regmap *regmap, unsigned int reg_ctrl,
unsigned int reg_data, unsigned int ram_offset,
unsigned int *data, size_t size);
void tegra210_ahub_read_ram(struct regmap *regmap, unsigned int reg_ctrl,
unsigned int reg_data, unsigned int ram_offset,
unsigned int *data, size_t size);
#endif

View File

@@ -0,0 +1,870 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_amx.c - Tegra210 AMX driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#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 <linux/version.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_amx.h"
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},
};
/**
* tegra210_amx_set_master_stream - set master stream and dependency
* @amx: struct of tegra210_amx
* @stream_id: one of input stream id to be a master
* @dependency: master dependency for tansferring
* 0 - wait on all, 1 - wait on any
*
* This dependency matter on starting point not every frame.
* Once amx starts to run, it is work as wait on all.
*/
static void tegra210_amx_set_master_stream(struct tegra210_amx *amx,
unsigned int stream_id,
unsigned int dependency)
{
unsigned int mask, val;
mask = (TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK |
TEGRA210_AMX_CTRL_RX_DEP_MASK);
val = (stream_id << TEGRA210_AMX_CTRL_MSTR_RX_NUN_SHIFT) |
(dependency << TEGRA210_AMX_CTRL_RX_DEP_SHIFT);
regmap_update_bits(amx->regmap, TEGRA210_AMX_CTRL, mask, val);
}
/**
* tegra210_amx_enable_instream - enable input stream
* @amx: struct of tegra210_amx
* @stream_id: amx input stream id for enabling
*/
static void tegra210_amx_enable_instream(struct tegra210_amx *amx,
unsigned int stream_id)
{
int reg;
reg = TEGRA210_AMX_CTRL;
regmap_update_bits(amx->regmap, reg,
TEGRA210_AMX_RX_ENABLE << stream_id,
TEGRA210_AMX_RX_ENABLE << stream_id);
}
/**
* tegra210_amx_disable_instream - disable input stream
* @amx: struct of tegra210_amx
* @stream_id: amx input stream id for disabling
*/
static void tegra210_amx_disable_instream(struct tegra210_amx *amx,
unsigned int stream_id)
{
int reg;
reg = TEGRA210_AMX_CTRL;
regmap_update_bits(amx->regmap, reg,
TEGRA210_AMX_RX_ENABLE << stream_id,
TEGRA210_AMX_RX_DISABLE);
}
/**
* tegra210_amx_set_out_byte_mask - set byte mask for output frame
* @amx: struct of tegra210_amx
* @mask1: enable for bytes 31 ~ 0
* @mask2: enable for bytes 63 ~ 32
*/
static void tegra210_amx_set_out_byte_mask(struct tegra210_amx *amx)
{
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]);
}
/**
* tegra210_amx_set_map_table - set map table not RAM
* @amx: struct of tegra210_amx
* @out_byte_addr: byte address in one frame
* @stream_id: input stream id (0 to 3)
* @nth_word: n-th word in the input stream (1 to 16)
* @nth_byte: n-th byte in the word (0 to 3)
*/
static void tegra210_amx_set_map_table(struct tegra210_amx *amx,
unsigned int out_byte_addr,
unsigned int stream_id,
unsigned int nth_word,
unsigned int nth_byte)
{
unsigned char *bytes_map = (unsigned char *)&amx->map;
bytes_map[out_byte_addr] =
(stream_id << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT) |
(nth_word << TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT) |
(nth_byte << TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT);
}
/**
* tegra210_amx_write_map_ram - write map information in RAM
* @amx: struct of tegra210_amx
* @addr: n-th word of input stream
* @val : bytes mapping information of the word
*/
static void tegra210_amx_write_map_ram(struct tegra210_amx *amx,
unsigned int addr,
unsigned int val)
{
unsigned int reg;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL,
(addr << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT));
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA, val);
regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &reg);
reg |= TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, reg);
regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &reg);
reg |= TEGRA210_AMX_CFG_CTRL_RW_WRITE;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, reg);
}
static void tegra210_amx_update_map_ram(struct tegra210_amx *amx)
{
int i;
for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++)
tegra210_amx_write_map_ram(amx, i, amx->map[i]);
}
static int tegra210_amx_stop(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct device *dev = cmpnt->dev;
struct tegra210_amx *amx = dev_get_drvdata(dev);
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(dev, "failed to stop AMX, err = %d\n", err);
return err;
}
/* SW reset */
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(dev, "failed to reset AMX, err = %d\n", err);
return err;
}
regmap_update_bits(amx->regmap, TEGRA210_AMX_SOFT_RESET,
TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK,
TEGRA210_AMX_SOFT_RESET_SOFT_DEFAULT);
return 0;
}
static int 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 unsigned int __maybe_unused
tegra210_amx_read_map_ram(struct tegra210_amx *amx, unsigned int addr)
{
unsigned int val;
int err;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL,
(addr << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT));
regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &val);
val |= TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN;
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, val);
regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, &val);
val &= ~(TEGRA210_AMX_CFG_CTRL_RW_WRITE);
regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL, val);
err = regmap_read_poll_timeout(amx->regmap,
TEGRA210_AMX_CFG_RAM_CTRL,
val, !(val & 0x80000000), 10, 10000);
if (err < 0)
return err;
regmap_read(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA, &val);
return val;
}
static int 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);
/* update map ram */
tegra210_amx_set_master_stream(amx, 0, TEGRA210_AMX_WAIT_ON_ANY);
tegra210_amx_update_map_ram(amx);
tegra210_amx_set_out_byte_mask(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);
if (strstr(dai->name, "OUT")) {
channels = amx->output_channels > 0 ?
amx->output_channels : channels;
} else {
channels = amx->input_channels[dai->id] > 0 ?
amx->input_channels[dai->id] : channels;
}
if (channels < 1 || channels > 16)
return -EINVAL;
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_S24_LE:
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)
{
int err;
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
/* For T19x soc frame period disable counter can be programmed as:
* counter = 1 * ahub_clk_rate
* -------------------------
* sample_rate
*
* TODO: read actual sample_rate & ahub_clk_rate
* For now using:
* sample_rate = 8000
* ahub_clk_rate = 49152000
*/
if (amx->soc_data->is_auto_disable_supported) {
regmap_write(amx->regmap,
TEGRA194_AMX_RX1_FRAME_PERIOD +
(dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE),
0x1800);
regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1);
}
err = tegra210_amx_set_audio_cif(dai, params,
TEGRA210_AMX_RX1_CIF_CTRL +
(dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE));
return err;
}
static int tegra210_amx_in_trigger(struct snd_pcm_substream *substream,
int cmd,
struct snd_soc_dai *dai)
{
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
tegra210_amx_enable_instream(amx, dai->id);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
tegra210_amx_disable_instream(amx, dai->id);
break;
default:
return -EINVAL;
}
return 0;
}
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_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct device *dev = dai->dev;
struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
unsigned int in_stream_idx, in_ch_idx, in_byte_idx;
int i;
if ((tx_num < 1) || (tx_num > 64)) {
dev_err(dev, "Doesn't support %d tx_num, need to be 1 to 64\n",
tx_num);
return -EINVAL;
}
if (!tx_slot) {
dev_err(dev, "tx_slot is NULL\n");
return -EINVAL;
}
memset(amx->map, 0, sizeof(amx->map));
memset(amx->byte_mask, 0, sizeof(amx->byte_mask));
for (i = 0; i < tx_num; i++) {
if (tx_slot[i] != 0) {
/* getting mapping information */
/* n-th input stream : 0 to 3 */
in_stream_idx = (tx_slot[i] >> 16) & 0x3;
/* n-th audio channel of input stream : 1 to 16 */
in_ch_idx = (tx_slot[i] >> 8) & 0x1f;
/* n-th byte of audio channel : 0 to 3 */
in_byte_idx = tx_slot[i] & 0x3;
tegra210_amx_set_map_table(amx, i, in_stream_idx,
in_ch_idx - 1,
in_byte_idx);
/* making byte_mask */
if (i > 31)
amx->byte_mask[1] |= (1 << (i - 32));
else
amx->byte_mask[0] |= (1 << i);
}
}
return 0;
}
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] = 256;
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 0;
}
static int tegra210_amx_get_channels(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);
int reg = mc->reg;
char buf[50];
snprintf(buf, 50, "Input%d Audio Channels", reg);
if (strstr(kcontrol->id.name, buf))
ucontrol->value.integer.value[0] = amx->input_channels[reg - 1];
else if (strstr(kcontrol->id.name, "Output Audio Channels"))
ucontrol->value.integer.value[0] = amx->output_channels;
return 0;
}
static int tegra210_amx_put_channels(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);
int reg = mc->reg;
int value = ucontrol->value.integer.value[0];
char buf[50];
snprintf(buf, 50, "Input%d Audio Channels", reg);
if (strstr(kcontrol->id.name, buf)) {
if (value >= 0 && value <= 16)
amx->input_channels[reg - 1] = value;
else
return -EINVAL;
} else if (strstr(kcontrol->id.name, "Output Audio Channels")) {
if (value >= 0 && value <= 16)
amx->output_channels = value;
else
return -EINVAL;
}
return 0;
}
static struct snd_soc_dai_ops tegra210_amx_out_dai_ops = {
.hw_params = tegra210_amx_out_hw_params,
.set_channel_map = tegra210_amx_set_channel_map,
};
static struct snd_soc_dai_ops tegra210_amx_in_dai_ops = {
.hw_params = tegra210_amx_in_hw_params,
.trigger = tegra210_amx_in_trigger,
};
#define IN_DAI(id) \
{ \
.name = "IN" #id, \
.playback = { \
.stream_name = "IN" #id " Receive", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.ops = &tegra210_amx_in_dai_ops, \
}
#define OUT_DAI(sname, dai_ops) \
{ \
.name = #sname, \
.capture = { \
.stream_name = #sname " Transmit", \
.channels_min = 1, \
.channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.ops = 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(OUT, &tegra210_amx_out_dai_ops),
};
static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = {
SND_SOC_DAPM_AIF_IN("IN1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0),
SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0),
SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0),
SND_SOC_DAPM_AIF_IN("IN4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0),
SND_SOC_DAPM_AIF_OUT_E("OUT", NULL, 0, TEGRA210_AMX_ENABLE,
TEGRA210_AMX_ENABLE_SHIFT, 0,
tegra210_amx_stop, SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route tegra210_amx_routes[] = {
{ "IN1", NULL, "IN1 Receive" },
{ "IN2", NULL, "IN2 Receive" },
{ "IN3", NULL, "IN3 Receive" },
{ "IN4", NULL, "IN4 Receive" },
{ "OUT", NULL, "IN1" },
{ "OUT", NULL, "IN2" },
{ "OUT", NULL, "IN3" },
{ "OUT", NULL, "IN4" },
{ "OUT Transmit", NULL, "OUT" },
};
#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)
#define TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(reg) \
SOC_SINGLE_EXT("Output Audio Channels", reg, 0, 16, 0, \
tegra210_amx_get_channels, \
tegra210_amx_put_channels)
#define TEGRA210_AMX_INPUT_CHANNELS_CTRL(reg) \
SOC_SINGLE_EXT("Input" #reg " Audio Channels", reg, 0, 16, 0, \
tegra210_amx_get_channels, \
tegra210_amx_put_channels)
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),
TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(1),
TEGRA210_AMX_INPUT_CHANNELS_CTRL(1),
TEGRA210_AMX_INPUT_CHANNELS_CTRL(2),
TEGRA210_AMX_INPUT_CHANNELS_CTRL(3),
TEGRA210_AMX_INPUT_CHANNELS_CTRL(4),
};
static 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),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
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,
.is_auto_disable_supported = false,
};
static const struct tegra210_amx_soc_data soc_data_tegra194 = {
.regmap_conf = &tegra194_amx_regmap_config,
.is_auto_disable_supported = 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;
memset(amx->map, 0, sizeof(amx->map));
memset(amx->byte_mask, 0, sizeof(amx->byte_mask));
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,116 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_amx.h - Definitions for Tegra210 AMX driver
*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_AMX_H__
#define __TEGRA210_AMX_H__
#define TEGRA210_AMX_AUDIOCIF_CH_STRIDE 4
/* 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_DBG 0xb4
#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8
#define TEGRA210_AMX_CFG_RAM_DATA 0xbc
#define TEGRA194_AMX_RX1_FRAME_PERIOD 0xc0
#define TEGRA194_AMX_RX2_FRAME_PERIOD 0xc4
#define TEGRA194_AMX_RX3_FRAME_PERIOD 0xc8
#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_NUN_SHIFT 14
#define TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK (3 << TEGRA210_AMX_CTRL_MSTR_RX_NUN_SHIFT)
#define TEGRA210_AMX_CTRL_RX_DEP_SHIFT 12
#define TEGRA210_AMX_CTRL_RX_DEP_MASK (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT)
#define TEGRA210_AMX_CTRL_RX_DEP_WT_ON_ALL 0
#define TEGRA210_AMX_CTRL_RX_DEP_WT_ON_ANY (1 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT)
#define TEGRA210_AMX_CTRL_RX_DEP_RSVD (3 << TEGRA210_AMX_CTRL_RX_DEP_SHIFT)
/* Fields in TEGRA210_AMX_CFG_RAM_CTRL */
#define TEGRA210_AMX_CFG_CTRL_RW_SHIFT 14
#define TEGRA210_AMX_CFG_CTRL_RW_MASK (1 << TEGRA210_AMX_CFG_CTRL_RW_SHIFT)
#define TEGRA210_AMX_CFG_CTRL_RW_WRITE (1 << TEGRA210_AMX_CFG_CTRL_RW_SHIFT)
#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN (1 << TEGRA210_AMX_CFG_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT 0
#define TEGRA210_AMX_CFG_CTRL_RAM_ADDR_MASK (0xff << TEGRA210_AMX_CFG_CTRL_RAM_ADDR_SHIFT)
/* Fields in TEGRA210_AMX_SOFT_RESET */
#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT 0
#define TEGRA210_AMX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT)
#define TEGRA210_AMX_SOFT_RESET_SOFT_EN (1 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT)
#define TEGRA210_AMX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_AMX_SOFT_RESET_SOFT_RESET_SHIFT)
/*
* Those defines are not in register field.
*/
#define TEGRA210_AMX_NUM_INPUTS 4
#define TEGRA210_AMX_RAM_DEPTH 16
#define TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT 6
#define TEGRA210_AMX_MAP_STREAM_NUMBER_MASK (0x3 << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT)
#define TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT 2
#define TEGRA210_AMX_MAP_WORD_NUMBER_MASK (0xF << TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT)
#define TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT 0
#define TEGRA210_AMX_MAP_BYTE_NUMBER_MASK (0x3 << TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT)
enum {
TEGRA210_AMX_WAIT_ON_ALL,
TEGRA210_AMX_WAIT_ON_ANY,
};
enum {
TEGRA210_AMX_RX_DISABLE,
TEGRA210_AMX_RX_ENABLE,
};
struct tegra210_amx_soc_data {
bool is_auto_disable_supported;
const struct regmap_config *regmap_conf;
};
struct tegra210_amx {
struct regmap *regmap;
unsigned int map[TEGRA210_AMX_RAM_DEPTH];
unsigned int byte_mask[2];
int input_channels[TEGRA210_AMX_NUM_INPUTS];
int output_channels;
const struct tegra210_amx_soc_data *soc_data;
};
#endif

View File

@@ -0,0 +1,717 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_dmic.c - Tegra210 DMIC driver
//
// Copyright (c) 2020-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/math64.h>
#include <linux/module.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_params.h>
#include <sound/soc.h>
#include "tegra210_dmic.h"
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
static const struct reg_default tegra210_dmic_reg_defaults[] = {
{ TEGRA210_DMIC_TX_INT_MASK, 0x00000001 },
{ TEGRA210_DMIC_TX_CIF_CTRL, 0x00007700 },
{ TEGRA210_DMIC_CG, 0x1 },
{ TEGRA210_DMIC_CTRL, 0x00000301 },
/* Below enables all filters - DCR, LP and SC */
{ TEGRA210_DMIC_DBG_CTRL, 0xe },
/* Below as per latest POR value */
{ TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4, 0x0 },
/* LP filter is configured for pass through and used to apply gain */
{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_0, 0x00800000 },
{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_1, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_2, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_3, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_0_COEF_4, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_0, 0x00800000 },
{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_1, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_2, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_3, 0x0 },
{ TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 },
};
static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev)
{
struct tegra210_dmic *dmic = dev_get_drvdata(dev);
regcache_cache_only(dmic->regmap, true);
regcache_mark_dirty(dmic->regmap);
clk_disable_unprepare(dmic->clk_dmic);
return 0;
}
static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev)
{
struct tegra210_dmic *dmic = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(dmic->clk_dmic);
if (err) {
dev_err(dev, "failed to enable DMIC clock, err: %d\n", err);
return err;
}
regcache_cache_only(dmic->regmap, false);
regcache_sync(dmic->regmap);
return 0;
}
static const unsigned int tegra210_dmic_fmts[] = {
0,
TEGRA_ACIF_BITS_16,
TEGRA_ACIF_BITS_32,
};
static int tegra210_dmic_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct tegra210_dmic *dmic = snd_soc_dai_get_drvdata(dai);
unsigned int srate, clk_rate, channels;
struct tegra_cif_conf cif_conf;
unsigned long long gain_q23 = DEFAULT_GAIN_Q23;
int err;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
cif_conf.audio_ch = channels;
if (dmic->audio_ch_override)
cif_conf.audio_ch = dmic->audio_ch_override;
switch (dmic->ch_select) {
case DMIC_CH_SELECT_LEFT:
case DMIC_CH_SELECT_RIGHT:
cif_conf.client_ch = 1;
break;
case DMIC_CH_SELECT_STEREO:
cif_conf.client_ch = 2;
break;
default:
dev_err(dai->dev, "invalid DMIC client channels\n");
return -EINVAL;
}
srate = params_rate(params);
if (dmic->srate_override)
srate = dmic->srate_override;
/*
* DMIC clock rate is a multiple of 'Over Sampling Ratio' and
* 'Sample Rate'. The supported OSR values are 64, 128 and 256.
*/
clk_rate = (DMIC_OSR_FACTOR << dmic->osr_val) * srate;
err = clk_set_rate(dmic->clk_dmic, clk_rate);
if (err) {
dev_err(dai->dev, "can't set DMIC clock rate %u, err: %d\n",
clk_rate, err);
return err;
}
regmap_update_bits(dmic->regmap,
/* Reg */
TEGRA210_DMIC_CTRL,
/* Mask */
TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK |
TEGRA210_DMIC_CTRL_OSR_MASK |
TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK,
/* Value */
(dmic->lrsel << LRSEL_POL_SHIFT) |
(dmic->osr_val << OSR_SHIFT) |
((dmic->ch_select + 1) << CH_SEL_SHIFT));
/*
* Use LP filter gain register to apply boost.
* Boost Gain Volume control has 100x factor.
*/
if (dmic->boost_gain)
gain_q23 = div_u64(gain_q23 * dmic->boost_gain, 100);
regmap_write(dmic->regmap, TEGRA210_DMIC_LP_FILTER_GAIN,
(unsigned int)gain_q23);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
dev_err(dai->dev, "unsupported format!\n");
return -EOPNOTSUPP;
}
if (dmic->audio_bits_override)
cif_conf.audio_bits =
tegra210_dmic_fmts[dmic->audio_bits_override];
cif_conf.client_bits = TEGRA_ACIF_BITS_24;
cif_conf.mono_conv = dmic->mono_to_stereo;
cif_conf.stereo_conv = dmic->stereo_to_mono;
tegra_set_cif(dmic->regmap, TEGRA210_DMIC_TX_CIF_CTRL, &cif_conf);
return 0;
}
static int tegra210_dmic_get_boost_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.integer.value[0] = dmic->boost_gain;
return 0;
}
static int tegra210_dmic_put_boost_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
int value = ucontrol->value.integer.value[0];
if (value == dmic->boost_gain)
return 0;
dmic->boost_gain = value;
return 1;
}
static int tegra210_dmic_get_ch_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->ch_select;
return 0;
}
static int tegra210_dmic_put_ch_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dmic->ch_select)
return 0;
dmic->ch_select = value;
return 1;
}
static int tegra210_dmic_get_mono_to_stereo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->mono_to_stereo;
return 0;
}
static int tegra210_dmic_put_mono_to_stereo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dmic->mono_to_stereo)
return 0;
dmic->mono_to_stereo = value;
return 1;
}
static int tegra210_dmic_get_stereo_to_mono(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->stereo_to_mono;
return 0;
}
static int tegra210_dmic_put_stereo_to_mono(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dmic->stereo_to_mono)
return 0;
dmic->stereo_to_mono = value;
return 1;
}
static int tegra210_dmic_get_audio_bitfmt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->audio_bits_override;
return 0;
}
static int tegra210_dmic_put_audio_bitfmt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (dmic->audio_bits_override == value)
return 0;
dmic->audio_bits_override = value;
return 1;
}
static int tegra210_dmic_get_audio_ch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.integer.value[0] = dmic->audio_ch_override;
return 0;
}
static int tegra210_dmic_put_audio_ch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
int value = ucontrol->value.integer.value[0];
if (dmic->audio_ch_override == value)
return 0;
dmic->audio_ch_override = value;
return 1;
}
static int tegra210_dmic_get_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.integer.value[0] = dmic->srate_override;
return 0;
}
static int tegra210_dmic_put_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
int value = ucontrol->value.integer.value[0];
if (dmic->srate_override == value)
return 0;
dmic->srate_override = value;
return 1;
}
static int tegra210_dmic_get_osr_val(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->osr_val;
return 0;
}
static int tegra210_dmic_put_osr_val(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dmic->osr_val)
return 0;
dmic->osr_val = value;
return 1;
}
static int tegra210_dmic_get_pol_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
ucontrol->value.enumerated.item[0] = dmic->lrsel;
return 0;
}
static int tegra210_dmic_put_pol_sel(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tegra210_dmic *dmic = snd_soc_component_get_drvdata(comp);
unsigned int value = ucontrol->value.enumerated.item[0];
if (value == dmic->lrsel)
return 0;
dmic->lrsel = value;
return 1;
}
static const struct snd_soc_dai_ops tegra210_dmic_dai_ops = {
.hw_params = tegra210_dmic_hw_params,
};
/*
* Three DAIs are exposed
* 1. "CIF" DAI for connecting with XBAR
* 2. "DAP" DAI for connecting with CODEC
* 3. "DUMMY_SOURCE" can be used when no external
* codec connection is available. In such case
* "DAP" is connected with "DUMMY_SOURCE"
*/
static struct snd_soc_dai_driver tegra210_dmic_dais[] = {
{
.name = "DMIC-CIF",
.capture = {
.stream_name = "CIF-Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "DMIC-DAP",
#if IS_ENABLED(CONFIG_TEGRA_DPCM)
.capture = {
.stream_name = "DAP-Capture",
#else
.playback = {
.stream_name = "DAP-Playback",
#endif
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &tegra210_dmic_dai_ops,
.symmetric_rate = 1,
},
{
.name = "DUMMY_SOURCE",
.capture = {
.stream_name = "Dummy-Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
};
static const struct snd_soc_dapm_widget tegra210_dmic_widgets[] = {
SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_DMIC_ENABLE, 0, 0),
SND_SOC_DAPM_MIC("MIC", NULL),
};
static const struct snd_soc_dapm_route tegra210_dmic_routes[] = {
#if IS_ENABLED(CONFIG_TEGRA_DPCM)
{ "XBAR-RX", NULL, "XBAR-Capture" },
{ "XBAR-Capture", NULL, "CIF-Capture" },
{ "CIF-Capture", NULL, "TX" },
{ "TX", NULL, "DAP-Capture" },
{ "DAP-Capture", NULL, "MIC" },
#else
{ "CIF-Capture", NULL, "TX" },
{ "TX", NULL, "DAP-Playback" },
{ "Dummy-Capture", NULL, "MIC" },
#endif
};
static const char * const tegra210_dmic_ch_select[] = {
"Left", "Right", "Stereo",
};
static const struct soc_enum tegra210_dmic_ch_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_ch_select),
tegra210_dmic_ch_select);
static const char * const tegra210_dmic_mono_conv_text[] = {
"Zero", "Copy",
};
static const char * const tegra210_dmic_stereo_conv_text[] = {
"CH0", "CH1", "AVG",
};
static const struct soc_enum tegra210_dmic_mono_conv_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_mono_conv_text),
tegra210_dmic_mono_conv_text);
static const struct soc_enum tegra210_dmic_stereo_conv_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_stereo_conv_text),
tegra210_dmic_stereo_conv_text);
static const char * const tegra210_dmic_format_text[] = {
"None",
"16",
"32",
};
static const struct soc_enum tegra210_dmic_format_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_format_text),
tegra210_dmic_format_text);
static const char * const tegra210_dmic_osr_text[] = {
"OSR_64", "OSR_128", "OSR_256",
};
static const struct soc_enum tegra210_dmic_osr_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_osr_text),
tegra210_dmic_osr_text);
static const char * const tegra210_dmic_lrsel_text[] = {
"Left", "Right",
};
static const struct soc_enum tegra210_dmic_lrsel_enum =
SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(tegra210_dmic_lrsel_text),
tegra210_dmic_lrsel_text);
static const struct snd_kcontrol_new tegra210_dmic_controls[] = {
SOC_SINGLE_EXT("Boost Gain Volume", 0, 0, MAX_BOOST_GAIN, 0,
tegra210_dmic_get_boost_gain,
tegra210_dmic_put_boost_gain),
SOC_ENUM_EXT("Channel Select", tegra210_dmic_ch_enum,
tegra210_dmic_get_ch_select, tegra210_dmic_put_ch_select),
SOC_ENUM_EXT("Mono To Stereo",
tegra210_dmic_mono_conv_enum,
tegra210_dmic_get_mono_to_stereo,
tegra210_dmic_put_mono_to_stereo),
SOC_ENUM_EXT("Stereo To Mono",
tegra210_dmic_stereo_conv_enum,
tegra210_dmic_get_stereo_to_mono,
tegra210_dmic_put_stereo_to_mono),
SOC_ENUM_EXT("Audio Bit Format", tegra210_dmic_format_enum,
tegra210_dmic_get_audio_bitfmt,
tegra210_dmic_put_audio_bitfmt),
SOC_SINGLE_EXT("Sample Rate", 0, 0, 48000, 0,
tegra210_dmic_get_sample_rate,
tegra210_dmic_put_sample_rate),
SOC_SINGLE_EXT("Audio Channels", 0, 0, 2, 0, tegra210_dmic_get_audio_ch,
tegra210_dmic_put_audio_ch),
SOC_ENUM_EXT("OSR Value", tegra210_dmic_osr_enum,
tegra210_dmic_get_osr_val, tegra210_dmic_put_osr_val),
SOC_ENUM_EXT("LR Polarity Select", tegra210_dmic_lrsel_enum,
tegra210_dmic_get_pol_sel, tegra210_dmic_put_pol_sel),
};
static const struct snd_soc_component_driver tegra210_dmic_compnt = {
.dapm_widgets = tegra210_dmic_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_dmic_widgets),
.dapm_routes = tegra210_dmic_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_dmic_routes),
.controls = tegra210_dmic_controls,
.num_controls = ARRAY_SIZE(tegra210_dmic_controls),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra210_dmic_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_DMIC_TX_INT_MASK ... TEGRA210_DMIC_TX_CIF_CTRL:
case TEGRA210_DMIC_ENABLE ... TEGRA210_DMIC_CG:
case TEGRA210_DMIC_CTRL:
case TEGRA210_DMIC_DBG_CTRL:
case TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 ... TEGRA210_DMIC_LP_BIQUAD_1_COEF_4:
return true;
default:
return false;
};
}
static bool tegra210_dmic_rd_reg(struct device *dev, unsigned int reg)
{
if (tegra210_dmic_wr_reg(dev, reg))
return true;
switch (reg) {
case TEGRA210_DMIC_TX_STATUS:
case TEGRA210_DMIC_TX_INT_STATUS:
case TEGRA210_DMIC_STATUS:
case TEGRA210_DMIC_INT_STATUS:
return true;
default:
return false;
};
}
static bool tegra210_dmic_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_DMIC_TX_STATUS:
case TEGRA210_DMIC_TX_INT_STATUS:
case TEGRA210_DMIC_TX_INT_SET:
case TEGRA210_DMIC_SOFT_RESET:
case TEGRA210_DMIC_STATUS:
case TEGRA210_DMIC_INT_STATUS:
return true;
default:
return false;
};
}
static const struct regmap_config tegra210_dmic_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_DMIC_LP_BIQUAD_1_COEF_4,
.writeable_reg = tegra210_dmic_wr_reg,
.readable_reg = tegra210_dmic_rd_reg,
.volatile_reg = tegra210_dmic_volatile_reg,
.reg_defaults = tegra210_dmic_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static int tegra210_dmic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_dmic *dmic;
void __iomem *regs;
int err;
dmic = devm_kzalloc(dev, sizeof(*dmic), GFP_KERNEL);
if (!dmic)
return -ENOMEM;
dmic->osr_val = DMIC_OSR_64;
dmic->ch_select = DMIC_CH_SELECT_STEREO;
dmic->lrsel = DMIC_LRSEL_LEFT;
dmic->boost_gain = 0;
dmic->stereo_to_mono = 0; /* "CH0" */
dev_set_drvdata(dev, dmic);
dmic->clk_dmic = devm_clk_get(dev, "dmic");
if (IS_ERR(dmic->clk_dmic)) {
dev_err(dev, "can't retrieve DMIC clock\n");
return PTR_ERR(dmic->clk_dmic);
}
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
dmic->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_dmic_regmap_config);
if (IS_ERR(dmic->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(dmic->regmap);
}
regcache_cache_only(dmic->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_dmic_compnt,
tegra210_dmic_dais,
ARRAY_SIZE(tegra210_dmic_dais));
if (err) {
dev_err(dev, "can't register DMIC component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_dmic_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_dmic_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend,
tegra210_dmic_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static const struct of_device_id tegra210_dmic_of_match[] = {
{ .compatible = "nvidia,tegra210-dmic" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_dmic_of_match);
static struct platform_driver tegra210_dmic_driver = {
.driver = {
.name = "tegra210-dmic",
.of_match_table = tegra210_dmic_of_match,
.pm = &tegra210_dmic_pm_ops,
},
.probe = tegra210_dmic_probe,
.remove = tegra210_dmic_remove,
};
module_platform_driver(tegra210_dmic_driver)
MODULE_AUTHOR("Rahul Mittal <rmittal@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 ASoC DMIC driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_dmic.h - Definitions for Tegra210 DMIC driver
*
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_DMIC_H__
#define __TEGRA210_DMIC_H__
/* Register offsets from DMIC BASE */
#define TEGRA210_DMIC_TX_STATUS 0x0c
#define TEGRA210_DMIC_TX_INT_STATUS 0x10
#define TEGRA210_DMIC_TX_INT_MASK 0x14
#define TEGRA210_DMIC_TX_INT_SET 0x18
#define TEGRA210_DMIC_TX_INT_CLEAR 0x1c
#define TEGRA210_DMIC_TX_CIF_CTRL 0x20
#define TEGRA210_DMIC_ENABLE 0x40
#define TEGRA210_DMIC_SOFT_RESET 0x44
#define TEGRA210_DMIC_CG 0x48
#define TEGRA210_DMIC_STATUS 0x4c
#define TEGRA210_DMIC_INT_STATUS 0x50
#define TEGRA210_DMIC_CTRL 0x64
#define TEGRA210_DMIC_DBG_CTRL 0x70
#define TEGRA210_DMIC_DCR_BIQUAD_0_COEF_4 0x88
#define TEGRA210_DMIC_LP_FILTER_GAIN 0x8c
#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_0 0x90
#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_1 0x94
#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_2 0x98
#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_3 0x9c
#define TEGRA210_DMIC_LP_BIQUAD_0_COEF_4 0xa0
#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_0 0xa4
#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_1 0xa8
#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_2 0xac
#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_3 0xb0
#define TEGRA210_DMIC_LP_BIQUAD_1_COEF_4 0xb4
/* Fields in TEGRA210_DMIC_CTRL */
#define CH_SEL_SHIFT 8
#define TEGRA210_DMIC_CTRL_CHANNEL_SELECT_MASK (0x3 << CH_SEL_SHIFT)
#define LRSEL_POL_SHIFT 4
#define TEGRA210_DMIC_CTRL_LRSEL_POLARITY_MASK (0x1 << LRSEL_POL_SHIFT)
#define OSR_SHIFT 0
#define TEGRA210_DMIC_CTRL_OSR_MASK (0x3 << OSR_SHIFT)
#define DMIC_OSR_FACTOR 64
#define DEFAULT_GAIN_Q23 0x800000
/* Max boost gain factor used for mixer control */
#define MAX_BOOST_GAIN 25599
enum tegra_dmic_ch_select {
DMIC_CH_SELECT_LEFT,
DMIC_CH_SELECT_RIGHT,
DMIC_CH_SELECT_STEREO,
};
enum tegra_dmic_osr {
DMIC_OSR_64,
DMIC_OSR_128,
DMIC_OSR_256,
};
enum tegra_dmic_lrsel {
DMIC_LRSEL_LEFT,
DMIC_LRSEL_RIGHT,
};
struct tegra210_dmic {
struct clk *clk_dmic;
struct regmap *regmap;
unsigned int audio_ch_override;
unsigned int audio_bits_override;
unsigned int srate_override;
unsigned int mono_to_stereo;
unsigned int stereo_to_mono;
unsigned int boost_gain;
unsigned int ch_select;
unsigned int osr_val;
unsigned int lrsel;
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2014-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// //
// tegra210_iqc.c - Tegra210 IQC driver // tegra210_iqc.c - Tegra210 IQC driver
//
// Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
@@ -19,6 +20,7 @@
#include <drivers-private/sound/soc/tegra/tegra_cif.h> #include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_iqc.h" #include "tegra210_iqc.h"
static const struct reg_default tegra210_iqc_reg_defaults[] = { static const struct reg_default tegra210_iqc_reg_defaults[] = {

View File

@@ -0,0 +1,912 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mbdrc.c - Tegra210 MBDRC driver
//
// Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "tegra210_ahub.h"
#include "tegra210_mbdrc.h"
#include "tegra210_ope.h"
#define MBDRC_FILTER_REG(reg, id) \
(reg + (id * TEGRA210_MBDRC_FILTER_PARAM_STRIDE))
#define MBDRC_FILTER_REG_DEFAULTS(id) \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_IIR_CONFIG, id), 0x00000005}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_ATTACK, id), 0x3e48590c}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_RELEASE, id), 0x08414e9f}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_ATTACK, id), 0x7fffffff}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_THRESHOLD, id), 0x06145082}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_OUT_THRESHOLD, id), 0x060d379b}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_1ST, id), 0x0000a000}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_2ND, id), 0x00002000}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_3RD, id), 0x00000b33}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_4TH, id), 0x00000800}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_5TH, id), 0x0000019a}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_MAKEUP_GAIN, id), 0x00000002}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_INIT_GAIN, id), 0x00066666}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_ATTACK, id), 0x00d9ba0e}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_RELEASE, id), 0x3e48590c}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_RELEASE, id), 0x7ffff26a}, \
{ MBDRC_FILTER_REG(TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL, id), 0x4000}
static const struct reg_default tegra210_mbdrc_reg_defaults[] = {
{ TEGRA210_MBDRC_CONFIG, 0x0030de51},
{ TEGRA210_MBDRC_CHANNEL_MASK, 0x00000003},
{ TEGRA210_MBDRC_FAST_FACTOR, 0x30000800},
MBDRC_FILTER_REG_DEFAULTS(0),
MBDRC_FILTER_REG_DEFAULTS(1),
MBDRC_FILTER_REG_DEFAULTS(2)
};
/* Default MBDRC parameters */
static const struct tegra210_mbdrc_config mbdrc_init_config = {
.mode = 0, /* bypass */
.rms_off = 48,
.peak_rms_mode = 1, /* PEAK */
.fliter_structure = 0, /* All-pass tree */
.shift_ctrl = 30,
.frame_size = 32,
.channel_mask = 0x3,
.fa_factor = 2048,
.fr_factor = 14747,
.band_params[MBDRC_LOW_BAND] = {
.band = MBDRC_LOW_BAND,
.iir_stages = 5,
.in_attack_tc = 1044928780,
.in_release_tc = 138497695,
.fast_attack_tc = 2147483647,
.in_threshold = {130, 80, 20, 6},
.out_threshold = {155, 55, 13, 6},
.ratio = {40960, 8192, 2867, 2048, 410},
.makeup_gain = 4,
.gain_init = 419430,
.gain_attack_tc = 14268942,
.gain_release_tc = 1440547090,
.fast_release_tc = 2147480170,
.biquad_params = {
/* Gains : b0, b1, a0, a1, a2 */
961046798, -2030431983, 1073741824, 2030431983, -961046798, /* band-0 */
1030244425, -2099481453, 1073741824, 2099481453, -1030244425, /* band-1 */
1067169294, -2136327263, 1073741824, 2136327263, -1067169294, /* band-2 */
434951949, -1306567134, 1073741824, 1306567134, -434951949, /* band-3 */
780656019, -1605955641, 1073741824, 1605955641, -780656019, /* band-4 */
1024497031, -1817128152, 1073741824, 1817128152, -1024497031, /* band-5 */
1073741824, 0, 0, 0, 0, /* band-6 */
1073741824, 0, 0, 0, 0, /* band-7 */
}
},
.band_params[MBDRC_MID_BAND] = {
.band = MBDRC_MID_BAND,
.iir_stages = 5,
.in_attack_tc = 1581413104,
.in_release_tc = 35494783,
.fast_attack_tc = 2147483647,
.in_threshold = {130, 50, 30, 6},
.out_threshold = {106, 50, 30, 13},
.ratio = {40960, 2867, 4096, 2867, 410},
.makeup_gain = 6,
.gain_init = 419430,
.gain_attack_tc = 4766887,
.gain_release_tc = 1044928780,
.fast_release_tc = 2147480170,
.biquad_params = {
/* Gains : b0, b1, a0, a1, a2 */
-1005668963, 1073741824, 0, 1005668963, 0, /* band-0 */
998437058, -2067742187, 1073741824, 2067742187, -998437058, /* band-1 */
1051963422, -2121153948, 1073741824, 2121153948, -1051963422, /* band-2 */
434951949, -1306567134, 1073741824, 1306567134, -434951949, /* band-3 */
780656019, -1605955641, 1073741824, 1605955641, -780656019, /* band-4 */
1024497031, -1817128152, 1073741824, 1817128152, -1024497031, /* band-5 */
1073741824, 0, 0, 0, 0, /* band-6 */
1073741824, 0, 0, 0, 0, /* band-7 */
}
},
.band_params[MBDRC_HIGH_BAND] = {
.band = MBDRC_HIGH_BAND,
.iir_stages = 5,
.in_attack_tc = 2144750688,
.in_release_tc = 70402888,
.fast_attack_tc = 2147483647,
.in_threshold = {130, 50, 30, 6},
.out_threshold = {106, 50, 30, 13},
.ratio = {40960, 2867, 4096, 2867, 410},
.makeup_gain = 6,
.gain_init = 419430,
.gain_attack_tc = 4766887,
.gain_release_tc = 1044928780,
.fast_release_tc = 2147480170,
.biquad_params = {
/* Gains : b0, b1, a0, a1, a2 */
1073741824, 0, 0, 0, 0, /* band-0 */
1073741824, 0, 0, 0, 0, /* band-1 */
1073741824, 0, 0, 0, 0, /* band-2 */
-619925131, 1073741824, 0, 619925131, 0, /* band-3 */
606839335, -1455425976, 1073741824, 1455425976, -606839335, /* band-4 */
917759617, -1724690840, 1073741824, 1724690840, -917759617, /* band-5 */
1073741824, 0, 0, 0, 0, /* band-6 */
1073741824, 0, 0, 0, 0, /* band-7 */
}
}
};
static int tegra210_mbdrc_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned long long fls_val = 1ULL << fls(mc->max);
unsigned int mask = fls_val - 1;
unsigned int val;
regmap_read(ope->mbdrc_regmap, mc->reg, &val);
ucontrol->value.integer.value[0] = (val >> mc->shift) & mask;
if (mc->invert)
ucontrol->value.integer.value[0] =
mc->max - ucontrol->value.integer.value[0];
return 0;
}
static int tegra210_mbdrc_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned long long fls_val = 1ULL << fls(mc->max);
unsigned int mask = fls_val - 1;
unsigned int val;
val = (ucontrol->value.integer.value[0] & mask);
if (mc->invert)
val = mc->max - val;
val = val << mc->shift;
return regmap_update_bits(ope->mbdrc_regmap, mc->reg,
(mask << mc->shift), val);
}
static int tegra210_mbdrc_get_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
regmap_read(ope->mbdrc_regmap, e->reg, &val);
ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask;
return 0;
}
static int tegra210_mbdrc_put_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
unsigned int mask;
if (ucontrol->value.enumerated.item[0] > e->items - 1)
return -EINVAL;
val = ucontrol->value.enumerated.item[0] << e->shift_l;
mask = e->mask << e->shift_l;
return regmap_update_bits(ope->mbdrc_regmap, e->reg, mask, val);
}
static int tegra210_mbdrc_band_params_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 *data = (u32 *)ucontrol->value.bytes.data;
u32 regs = params->soc.base;
u32 mask = params->soc.mask;
u32 shift = params->shift;
int i;
for (i = 0; i < params->soc.num_regs; i++,
regs += cmpnt->val_bytes) {
regmap_read(ope->mbdrc_regmap, regs, &data[i]);
data[i] = ((data[i] & mask) >> shift);
}
return 0;
}
static int tegra210_mbdrc_band_params_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 *data = (u32 *)ucontrol->value.bytes.data;
u32 regs = params->soc.base;
u32 mask = params->soc.mask;
u32 shift = params->shift;
int i;
for (i = 0; i < params->soc.num_regs; i++,
regs += cmpnt->val_bytes)
regmap_update_bits(ope->mbdrc_regmap, regs, mask,
data[i] << shift);
return 0;
}
static int tegra210_mbdrc_threshold_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 *data = (u32 *)ucontrol->value.bytes.data;
u32 regs = params->soc.base;
u32 num_regs = params->soc.num_regs;
u32 val;
int i;
for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) {
regmap_read(ope->mbdrc_regmap, regs, &val);
data[i] = (val & TEGRA210_MBDRC_THRESH_1ST_MASK) >>
TEGRA210_MBDRC_THRESH_1ST_SHIFT;
data[i + 1] = (val & TEGRA210_MBDRC_THRESH_2ND_MASK) >>
TEGRA210_MBDRC_THRESH_2ND_SHIFT;
data[i + 2] = (val & TEGRA210_MBDRC_THRESH_3RD_MASK) >>
TEGRA210_MBDRC_THRESH_3RD_SHIFT;
data[i + 3] = (val & TEGRA210_MBDRC_THRESH_4TH_MASK) >>
TEGRA210_MBDRC_THRESH_4TH_SHIFT;
}
return 0;
}
static int tegra210_mbdrc_threshold_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 *data = (u32 *)ucontrol->value.bytes.data;
u32 regs = params->soc.base;
u32 num_regs = params->soc.num_regs;
int i;
for (i = 0; i < num_regs; i += 4, regs += cmpnt->val_bytes) {
data[i] = (((data[i] >> TEGRA210_MBDRC_THRESH_1ST_SHIFT) &
TEGRA210_MBDRC_THRESH_1ST_MASK) |
((data[i + 1] >> TEGRA210_MBDRC_THRESH_2ND_SHIFT) &
TEGRA210_MBDRC_THRESH_2ND_MASK) |
((data[i + 2] >> TEGRA210_MBDRC_THRESH_3RD_SHIFT) &
TEGRA210_MBDRC_THRESH_3RD_MASK) |
((data[i + 3] >> TEGRA210_MBDRC_THRESH_4TH_SHIFT) &
TEGRA210_MBDRC_THRESH_4TH_MASK));
regmap_update_bits(ope->mbdrc_regmap, regs,
0xffffffff, data[i]);
}
return 0;
}
static int tegra210_mbdrc_biquad_coeffs_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
u32 *data = (u32 *)ucontrol->value.bytes.data;
memset(data, 0, params->soc.num_regs * cmpnt->val_bytes);
return 0;
}
static int tegra210_mbdrc_biquad_coeffs_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 reg_ctrl = params->soc.base;
u32 reg_data = reg_ctrl + cmpnt->val_bytes;
u32 *data = (u32 *)ucontrol->value.bytes.data;
tegra210_ahub_write_ram(ope->mbdrc_regmap, reg_ctrl, reg_data,
params->shift, data, params->soc.num_regs);
return 0;
}
static int tegra210_mbdrc_param_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_bytes *params = (void *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = params->num_regs;
return 0;
}
static int tegra210_mbdrc_vol_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned long long fls_val = 1ULL << fls(mc->max);
unsigned int mask = fls_val - 1;
int val;
regmap_read(ope->mbdrc_regmap, mc->reg, &val);
ucontrol->value.integer.value[0] = ((val >> mc->shift) -
TEGRA210_MBDRC_MASTER_VOL_MIN) & mask;
return 0;
}
static int tegra210_mbdrc_vol_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned long long fls_val = 1ULL << fls(mc->max);
unsigned int mask = fls_val - 1;
unsigned int val;
val = (ucontrol->value.integer.value[0] & mask);
val = val + TEGRA210_MBDRC_MASTER_VOL_MIN;
val = val << mc->shift;
return regmap_write(ope->mbdrc_regmap, mc->reg, val);
}
static const char * const tegra210_mbdrc_mode_text[] = {
"bypass", "fullband", "dualband", "multiband"
};
static const struct soc_enum tegra210_mbdrc_mode_enum =
SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT, 4,
tegra210_mbdrc_mode_text);
static const char * const tegra210_mbdrc_peak_rms_text[] = {
"peak", "rms"
};
static const struct soc_enum tegra210_mbdrc_peak_rms_enum =
SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT, 2,
tegra210_mbdrc_peak_rms_text);
static const char * const tegra210_mbdrc_filter_structure_text[] = {
"all-pass-tree", "flexible"
};
static const struct soc_enum tegra210_mbdrc_filter_structure_enum =
SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT, 2,
tegra210_mbdrc_filter_structure_text);
static const char * const tegra210_mbdrc_frame_size_text[] = {
"N1", "N2", "N4", "N8", "N16", "N32", "N64"
};
static const struct soc_enum tegra210_mbdrc_frame_size_enum =
SOC_ENUM_SINGLE(TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT, 7,
tegra210_mbdrc_frame_size_text);
#define TEGRA_MBDRC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, xinfo) \
TEGRA_SOC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, \
tegra210_mbdrc_band_params_get, tegra210_mbdrc_band_params_put, \
tegra210_mbdrc_param_info)
#define TEGRA_MBDRC_BAND_BYTES_EXT(xname, xbase, xshift, xmask, xinfo) \
TEGRA_MBDRC_BYTES_EXT(xname, xbase, TEGRA210_MBDRC_FILTER_COUNT, \
xshift, xmask, xinfo)
static const DECLARE_TLV_DB_MINMAX(mdbrc_vol_tlv, -25600, 25500);
static const struct snd_kcontrol_new tegra210_mbdrc_controls[] = {
SOC_ENUM_EXT("MBDRC Peak RMS Mode", tegra210_mbdrc_peak_rms_enum,
tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum),
SOC_ENUM_EXT("MBDRC Filter Structure",
tegra210_mbdrc_filter_structure_enum,
tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum),
SOC_ENUM_EXT("MBDRC Frame Size", tegra210_mbdrc_frame_size_enum,
tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum),
SOC_ENUM_EXT("MBDRC Mode", tegra210_mbdrc_mode_enum,
tegra210_mbdrc_get_enum, tegra210_mbdrc_put_enum),
SOC_SINGLE_EXT("MBDRC RMS Offset", TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT, 0x1ff, 0,
tegra210_mbdrc_get, tegra210_mbdrc_put),
SOC_SINGLE_EXT("MBDRC Shift Control", TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT, 0x1f, 0,
tegra210_mbdrc_get, tegra210_mbdrc_put),
SOC_SINGLE_EXT("MBDRC Fast Attack Factor", TEGRA210_MBDRC_FAST_FACTOR,
TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT, 0xffff, 0,
tegra210_mbdrc_get, tegra210_mbdrc_put),
SOC_SINGLE_EXT("MBDRC Fast Release Factor", TEGRA210_MBDRC_FAST_FACTOR,
TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT, 0xffff, 0,
tegra210_mbdrc_get, tegra210_mbdrc_put),
SOC_SINGLE_RANGE_EXT_TLV("MBDRC Master Volume", TEGRA210_MBDRC_MASTER_VOLUME,
TEGRA210_MBDRC_MASTER_VOLUME_SHIFT, TEGRA210_MBDRC_MASTER_VOL_MIN,
TEGRA210_MBDRC_MASTER_VOL_MAX, 0,
tegra210_mbdrc_vol_get, tegra210_mbdrc_vol_put, mdbrc_vol_tlv),
TEGRA_SOC_BYTES_EXT("MBDRC IIR Stages", TEGRA210_MBDRC_IIR_CONFIG,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT,
TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC In Attack Time Const", TEGRA210_MBDRC_IN_ATTACK,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT,
TEGRA210_MBDRC_IN_ATTACK_TC_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC In Release Time Const", TEGRA210_MBDRC_IN_RELEASE,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT,
TEGRA210_MBDRC_IN_RELEASE_TC_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Fast Attack Time Const", TEGRA210_MBDRC_FAST_ATTACK,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT,
TEGRA210_MBDRC_FAST_ATTACK_TC_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC In Threshold", TEGRA210_MBDRC_IN_THRESHOLD,
TEGRA210_MBDRC_FILTER_COUNT * 4, 0, 0xffffffff,
tegra210_mbdrc_threshold_get, tegra210_mbdrc_threshold_put, tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Out Threshold", TEGRA210_MBDRC_OUT_THRESHOLD,
TEGRA210_MBDRC_FILTER_COUNT * 4, 0, 0xffffffff,
tegra210_mbdrc_threshold_get, tegra210_mbdrc_threshold_put, tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Ratio", TEGRA210_MBDRC_RATIO_1ST,
TEGRA210_MBDRC_FILTER_COUNT * 5,
TEGRA210_MBDRC_RATIO_1ST_SHIFT, TEGRA210_MBDRC_RATIO_1ST_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Makeup Gain", TEGRA210_MBDRC_MAKEUP_GAIN,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT,
TEGRA210_MBDRC_MAKEUP_GAIN_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Init Gain", TEGRA210_MBDRC_INIT_GAIN,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_INIT_GAIN_SHIFT,
TEGRA210_MBDRC_INIT_GAIN_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Attack Gain", TEGRA210_MBDRC_GAIN_ATTACK,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_GAIN_ATTACK_SHIFT,
TEGRA210_MBDRC_GAIN_ATTACK_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Release Gain", TEGRA210_MBDRC_GAIN_RELEASE,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_GAIN_RELEASE_SHIFT,
TEGRA210_MBDRC_GAIN_RELEASE_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Fast Release Gain",
TEGRA210_MBDRC_FAST_RELEASE,
TEGRA210_MBDRC_FILTER_COUNT,
TEGRA210_MBDRC_FAST_RELEASE_SHIFT,
TEGRA210_MBDRC_FAST_RELEASE_MASK,
tegra210_mbdrc_band_params_get,
tegra210_mbdrc_band_params_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Low Band Biquad Coeffs",
TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL,
TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff,
tegra210_mbdrc_biquad_coeffs_get,
tegra210_mbdrc_biquad_coeffs_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC Mid Band Biquad Coeffs",
TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL +
TEGRA210_MBDRC_FILTER_PARAM_STRIDE,
TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff,
tegra210_mbdrc_biquad_coeffs_get,
tegra210_mbdrc_biquad_coeffs_put,
tegra210_mbdrc_param_info),
TEGRA_SOC_BYTES_EXT("MBDRC High Band Biquad Coeffs",
TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL +
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE * 2),
TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5, 0, 0xffffffff,
tegra210_mbdrc_biquad_coeffs_get,
tegra210_mbdrc_biquad_coeffs_put,
tegra210_mbdrc_param_info),
};
static bool tegra210_mbdrc_wr_reg(struct device *dev, unsigned int reg)
{
if (reg >= TEGRA210_MBDRC_IIR_CONFIG)
reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) %
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE *
TEGRA210_MBDRC_FILTER_COUNT));
switch (reg) {
case TEGRA210_MBDRC_CG:
case TEGRA210_MBDRC_SOFT_RESET:
case TEGRA210_MBDRC_CONFIG:
case TEGRA210_MBDRC_CHANNEL_MASK:
case TEGRA210_MBDRC_MASTER_VOLUME:
case TEGRA210_MBDRC_FAST_FACTOR:
case TEGRA210_MBDRC_IIR_CONFIG:
case TEGRA210_MBDRC_IN_ATTACK:
case TEGRA210_MBDRC_IN_RELEASE:
case TEGRA210_MBDRC_FAST_ATTACK:
case TEGRA210_MBDRC_IN_THRESHOLD:
case TEGRA210_MBDRC_OUT_THRESHOLD:
case TEGRA210_MBDRC_RATIO_1ST:
case TEGRA210_MBDRC_RATIO_2ND:
case TEGRA210_MBDRC_RATIO_3RD:
case TEGRA210_MBDRC_RATIO_4TH:
case TEGRA210_MBDRC_RATIO_5TH:
case TEGRA210_MBDRC_MAKEUP_GAIN:
case TEGRA210_MBDRC_INIT_GAIN:
case TEGRA210_MBDRC_GAIN_ATTACK:
case TEGRA210_MBDRC_GAIN_RELEASE:
case TEGRA210_MBDRC_FAST_RELEASE:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_mbdrc_rd_reg(struct device *dev, unsigned int reg)
{
if (reg >= TEGRA210_MBDRC_IIR_CONFIG)
reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) %
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE *
TEGRA210_MBDRC_FILTER_COUNT));
switch (reg) {
case TEGRA210_MBDRC_CG:
case TEGRA210_MBDRC_SOFT_RESET:
case TEGRA210_MBDRC_STATUS:
case TEGRA210_MBDRC_CONFIG:
case TEGRA210_MBDRC_CHANNEL_MASK:
case TEGRA210_MBDRC_MASTER_VOLUME:
case TEGRA210_MBDRC_FAST_FACTOR:
case TEGRA210_MBDRC_IIR_CONFIG:
case TEGRA210_MBDRC_IN_ATTACK:
case TEGRA210_MBDRC_IN_RELEASE:
case TEGRA210_MBDRC_FAST_ATTACK:
case TEGRA210_MBDRC_IN_THRESHOLD:
case TEGRA210_MBDRC_OUT_THRESHOLD:
case TEGRA210_MBDRC_RATIO_1ST:
case TEGRA210_MBDRC_RATIO_2ND:
case TEGRA210_MBDRC_RATIO_3RD:
case TEGRA210_MBDRC_RATIO_4TH:
case TEGRA210_MBDRC_RATIO_5TH:
case TEGRA210_MBDRC_MAKEUP_GAIN:
case TEGRA210_MBDRC_INIT_GAIN:
case TEGRA210_MBDRC_GAIN_ATTACK:
case TEGRA210_MBDRC_GAIN_RELEASE:
case TEGRA210_MBDRC_FAST_RELEASE:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_mbdrc_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg >= TEGRA210_MBDRC_IIR_CONFIG)
reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) %
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE *
TEGRA210_MBDRC_FILTER_COUNT));
switch (reg) {
case TEGRA210_MBDRC_SOFT_RESET:
case TEGRA210_MBDRC_STATUS:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA:
return true;
default:
return false;
}
}
static bool tegra210_mbdrc_precious_reg(struct device *dev, unsigned int reg)
{
if (reg >= TEGRA210_MBDRC_IIR_CONFIG)
reg -= ((reg - TEGRA210_MBDRC_IIR_CONFIG) %
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE *
TEGRA210_MBDRC_FILTER_COUNT));
switch (reg) {
case TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA:
return true;
default:
return false;
}
}
static const struct regmap_config tegra210_mbdrc_regmap_config = {
.name = "mbdrc",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_MBDRC_MAX_REG,
.writeable_reg = tegra210_mbdrc_wr_reg,
.readable_reg = tegra210_mbdrc_rd_reg,
.volatile_reg = tegra210_mbdrc_volatile_reg,
.precious_reg = tegra210_mbdrc_precious_reg,
.reg_defaults = tegra210_mbdrc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mbdrc_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
int tegra210_mbdrc_hw_params(struct snd_soc_component *cmpnt)
{
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
const struct tegra210_mbdrc_config *conf = &mbdrc_init_config;
u32 val = 0;
int i;
regmap_read(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG, &val);
if (val & TEGRA210_MBDRC_CONFIG_MBDRC_MODE_BYPASS)
return 0;
for (i = 0; i < MBDRC_NUM_BAND; i++) {
const struct tegra210_mbdrc_band_params *params =
&conf->band_params[i];
u32 reg_off = i * TEGRA210_MBDRC_FILTER_PARAM_STRIDE;
tegra210_ahub_write_ram(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL,
reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA, 0,
(u32 *)&params->biquad_params[0],
TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5);
}
return 0;
}
EXPORT_SYMBOL_GPL(tegra210_mbdrc_hw_params);
int tegra210_mbdrc_codec_init(struct snd_soc_component *cmpnt)
{
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
const struct tegra210_mbdrc_config *conf = &mbdrc_init_config;
u32 val;
int i;
pm_runtime_get_sync(cmpnt->dev);
/* Initialize MBDRC registers and ahub-ram with default params */
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MASK,
conf->mode << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_RMS_OFFSET_MASK,
conf->rms_off << TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_PEAK_RMS_MASK,
conf->peak_rms_mode << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_MASK,
conf->fliter_structure <<
TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_MASK,
conf->shift_ctrl << TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CONFIG,
TEGRA210_MBDRC_CONFIG_FRAME_SIZE_MASK,
__ffs(conf->frame_size) <<
TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_CHANNEL_MASK,
TEGRA210_MBDRC_CHANNEL_MASK_MASK,
conf->channel_mask << TEGRA210_MBDRC_CHANNEL_MASK_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_FAST_FACTOR,
TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK,
conf->fa_factor << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT);
regmap_update_bits(ope->mbdrc_regmap, TEGRA210_MBDRC_FAST_FACTOR,
TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK,
conf->fr_factor << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT);
for (i = 0; i < MBDRC_NUM_BAND; i++) {
const struct tegra210_mbdrc_band_params *params =
&conf->band_params[i];
u32 reg_off = i * TEGRA210_MBDRC_FILTER_PARAM_STRIDE;
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_IIR_CONFIG,
TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK,
params->iir_stages <<
TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_IN_ATTACK,
TEGRA210_MBDRC_IN_ATTACK_TC_MASK,
params->in_attack_tc <<
TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_IN_RELEASE,
TEGRA210_MBDRC_IN_RELEASE_TC_MASK,
params->in_release_tc <<
TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_FAST_ATTACK,
TEGRA210_MBDRC_FAST_ATTACK_TC_MASK,
params->fast_attack_tc <<
TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT);
val = (((params->in_threshold[0] >>
TEGRA210_MBDRC_THRESH_1ST_SHIFT) &
TEGRA210_MBDRC_THRESH_1ST_MASK) |
((params->in_threshold[1] >>
TEGRA210_MBDRC_THRESH_2ND_SHIFT) &
TEGRA210_MBDRC_THRESH_2ND_MASK) |
((params->in_threshold[2] >>
TEGRA210_MBDRC_THRESH_3RD_SHIFT) &
TEGRA210_MBDRC_THRESH_3RD_MASK) |
((params->in_threshold[3] >>
TEGRA210_MBDRC_THRESH_4TH_SHIFT) &
TEGRA210_MBDRC_THRESH_4TH_MASK));
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_IN_THRESHOLD, 0xffffffff, val);
val = (((params->out_threshold[0] >>
TEGRA210_MBDRC_THRESH_1ST_SHIFT) &
TEGRA210_MBDRC_THRESH_1ST_MASK) |
((params->out_threshold[1] >>
TEGRA210_MBDRC_THRESH_2ND_SHIFT) &
TEGRA210_MBDRC_THRESH_2ND_MASK) |
((params->out_threshold[2] >>
TEGRA210_MBDRC_THRESH_3RD_SHIFT) &
TEGRA210_MBDRC_THRESH_3RD_MASK) |
((params->out_threshold[3] >>
TEGRA210_MBDRC_THRESH_4TH_SHIFT) &
TEGRA210_MBDRC_THRESH_4TH_MASK));
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_OUT_THRESHOLD,
0xffffffff, val);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_RATIO_1ST,
TEGRA210_MBDRC_RATIO_1ST_MASK,
params->ratio[0] << TEGRA210_MBDRC_RATIO_1ST_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_RATIO_2ND,
TEGRA210_MBDRC_RATIO_2ND_MASK,
params->ratio[1] << TEGRA210_MBDRC_RATIO_2ND_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_RATIO_3RD,
TEGRA210_MBDRC_RATIO_3RD_MASK,
params->ratio[2] << TEGRA210_MBDRC_RATIO_3RD_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_RATIO_4TH,
TEGRA210_MBDRC_RATIO_4TH_MASK,
params->ratio[3] << TEGRA210_MBDRC_RATIO_4TH_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_RATIO_5TH,
TEGRA210_MBDRC_RATIO_5TH_MASK,
params->ratio[4] << TEGRA210_MBDRC_RATIO_5TH_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_MAKEUP_GAIN,
TEGRA210_MBDRC_MAKEUP_GAIN_MASK,
params->makeup_gain <<
TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_INIT_GAIN,
TEGRA210_MBDRC_INIT_GAIN_MASK,
params->gain_init <<
TEGRA210_MBDRC_INIT_GAIN_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_GAIN_ATTACK,
TEGRA210_MBDRC_GAIN_ATTACK_MASK,
params->gain_attack_tc <<
TEGRA210_MBDRC_GAIN_ATTACK_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_GAIN_RELEASE,
TEGRA210_MBDRC_GAIN_RELEASE_MASK,
params->gain_release_tc <<
TEGRA210_MBDRC_GAIN_RELEASE_SHIFT);
regmap_update_bits(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_FAST_RELEASE,
TEGRA210_MBDRC_FAST_RELEASE_MASK,
params->fast_release_tc <<
TEGRA210_MBDRC_FAST_RELEASE_SHIFT);
tegra210_ahub_write_ram(ope->mbdrc_regmap,
reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL,
reg_off + TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA, 0,
(u32 *)&params->biquad_params[0],
TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5);
}
pm_runtime_put_sync(cmpnt->dev);
snd_soc_add_component_controls(cmpnt, tegra210_mbdrc_controls,
ARRAY_SIZE(tegra210_mbdrc_controls));
return 0;
}
EXPORT_SYMBOL_GPL(tegra210_mbdrc_codec_init);
int tegra210_mbdrc_regmap_init(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_ope *ope = dev_get_drvdata(dev);
struct device_node *child;
struct resource mem;
void __iomem *regs;
int err;
child = of_get_child_by_name(dev->of_node, "dynamic-range-compressor");
if (!child)
return -ENODEV;
err = of_address_to_resource(child, 0, &mem);
of_node_put(child);
if (err < 0) {
dev_err(dev, "fail to get MBDRC resource\n");
return err;
}
mem.flags = IORESOURCE_MEM;
regs = devm_ioremap_resource(&pdev->dev, &mem);
if (IS_ERR(regs))
return PTR_ERR(regs);
ope->mbdrc_regmap =
devm_regmap_init_mmio(&pdev->dev, regs,
&tegra210_mbdrc_regmap_config);
if (IS_ERR(ope->mbdrc_regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(ope->mbdrc_regmap);
}
regcache_cache_only(ope->mbdrc_regmap, true);
return 0;
}
EXPORT_SYMBOL_GPL(tegra210_mbdrc_regmap_init);
MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 MBDRC module");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_mbdrc.h - Definitions for Tegra210 MBDRC driver
*
* Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_MBDRC_H__
#define __TEGRA210_MBDRC_H__
/* Order of these enums are same as the order of band specific hw registers */
enum {
MBDRC_LOW_BAND,
MBDRC_MID_BAND,
MBDRC_HIGH_BAND,
MBDRC_NUM_BAND,
};
/* Register offsets from TEGRA210_MBDRC*_BASE */
#define TEGRA210_MBDRC_SOFT_RESET 0x4
#define TEGRA210_MBDRC_CG 0x8
#define TEGRA210_MBDRC_STATUS 0xc
#define TEGRA210_MBDRC_CONFIG 0x28
#define TEGRA210_MBDRC_CHANNEL_MASK 0x2c
#define TEGRA210_MBDRC_MASTER_VOLUME 0x30
#define TEGRA210_MBDRC_FAST_FACTOR 0x34
#define TEGRA210_MBDRC_FILTER_COUNT 3
#define TEGRA210_MBDRC_FILTER_PARAM_STRIDE 0x4
#define TEGRA210_MBDRC_IIR_CONFIG 0x38
#define TEGRA210_MBDRC_IN_ATTACK 0x44
#define TEGRA210_MBDRC_IN_RELEASE 0x50
#define TEGRA210_MBDRC_FAST_ATTACK 0x5c
#define TEGRA210_MBDRC_IN_THRESHOLD 0x68
#define TEGRA210_MBDRC_OUT_THRESHOLD 0x74
#define TEGRA210_MBDRC_RATIO_1ST 0x80
#define TEGRA210_MBDRC_RATIO_2ND 0x8c
#define TEGRA210_MBDRC_RATIO_3RD 0x98
#define TEGRA210_MBDRC_RATIO_4TH 0xa4
#define TEGRA210_MBDRC_RATIO_5TH 0xb0
#define TEGRA210_MBDRC_MAKEUP_GAIN 0xbc
#define TEGRA210_MBDRC_INIT_GAIN 0xc8
#define TEGRA210_MBDRC_GAIN_ATTACK 0xd4
#define TEGRA210_MBDRC_GAIN_RELEASE 0xe0
#define TEGRA210_MBDRC_FAST_RELEASE 0xec
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL 0xf8
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA 0x104
#define TEGRA210_MBDRC_MAX_REG (TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_DATA + \
(TEGRA210_MBDRC_FILTER_PARAM_STRIDE * \
(TEGRA210_MBDRC_FILTER_COUNT - 1)))
/* Fields for TEGRA210_MBDRC_CONFIG */
#define TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT 16
#define TEGRA210_MBDRC_CONFIG_RMS_OFFSET_MASK (0x1ff << TEGRA210_MBDRC_CONFIG_RMS_OFFSET_SHIFT)
#define TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT 14
#define TEGRA210_MBDRC_CONFIG_PEAK_RMS_MASK (0x1 << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT)
#define TEGRA210_MBDRC_CONFIG_PEAK (1 << TEGRA210_MBDRC_CONFIG_PEAK_RMS_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT 13
#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_MASK (0x1 << TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_FLEX (1 << TEGRA210_MBDRC_CONFIG_FILTER_STRUCTURE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT 8
#define TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_MASK (0x1f << TEGRA210_MBDRC_CONFIG_SHIFT_CTRL_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT 4
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_MASK (0xf << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N1 (0 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N2 (1 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N4 (2 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N8 (3 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N16 (4 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N32 (5 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_FRAME_SIZE_N64 (6 << TEGRA210_MBDRC_CONFIG_FRAME_SIZE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT 0
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MASK (0x3 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_BYPASS (0 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_FULLBAND (1 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_DUALBAND (2 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT)
#define TEGRA210_MBDRC_CONFIG_MBDRC_MODE_MULTIBAND (3 << TEGRA210_MBDRC_CONFIG_MBDRC_MODE_SHIFT)
/* Fields for TEGRA210_MBDRC_CHANNEL_MASK */
#define TEGRA210_MBDRC_CHANNEL_MASK_SHIFT 0
#define TEGRA210_MBDRC_CHANNEL_MASK_MASK (0xff << TEGRA210_MBDRC_CHANNEL_MASK_SHIFT)
/* Fields for TEGRA210_MBDRC_MASTER_VOLUME */
#define TEGRA210_MBDRC_MASTER_VOLUME_SHIFT 23
#define TEGRA210_MBDRC_MASTER_VOL_MIN -256
#define TEGRA210_MBDRC_MASTER_VOL_MAX 256
/* Fields for TEGRA210_MBDRC_FAST_FACTOR */
#define TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT 16
#define TEGRA210_MBDRC_FAST_FACTOR_RELEASE_MASK (0xffff << TEGRA210_MBDRC_FAST_FACTOR_RELEASE_SHIFT)
#define TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT 0
#define TEGRA210_MBDRC_FAST_FACTOR_ATTACK_MASK (0xffff << TEGRA210_MBDRC_FAST_FACTOR_ATTACK_SHIFT)
/* Fields for TEGRA210_MBDRC_IIR_CONFIG */
#define TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT 0
#define TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_MASK (0xf << TEGRA210_MBDRC_IIR_CONFIG_NUM_STAGES_SHIFT)
/* Fields for TEGRA210_MBDRC_IN_ATTACK */
#define TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT 0
#define TEGRA210_MBDRC_IN_ATTACK_TC_MASK (0xffffffff << TEGRA210_MBDRC_IN_ATTACK_TC_SHIFT)
/* Fields for TEGRA210_MBDRC_IN_RELEASE */
#define TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT 0
#define TEGRA210_MBDRC_IN_RELEASE_TC_MASK (0xffffffff << TEGRA210_MBDRC_IN_RELEASE_TC_SHIFT)
/* Fields for TEGRA210_MBDRC_FAST_ATTACK */
#define TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT 0
#define TEGRA210_MBDRC_FAST_ATTACK_TC_MASK (0xffffffff << TEGRA210_MBDRC_FAST_ATTACK_TC_SHIFT)
/* Fields for TEGRA210_MBDRC_IN_THRESHOLD / TEGRA210_MBDRC_OUT_THRESHOLD */
#define TEGRA210_MBDRC_THRESH_4TH_SHIFT 24
#define TEGRA210_MBDRC_THRESH_4TH_MASK (0xff << TEGRA210_MBDRC_THRESH_4TH_SHIFT)
#define TEGRA210_MBDRC_THRESH_3RD_SHIFT 16
#define TEGRA210_MBDRC_THRESH_3RD_MASK (0xff << TEGRA210_MBDRC_THRESH_3RD_SHIFT)
#define TEGRA210_MBDRC_THRESH_2ND_SHIFT 8
#define TEGRA210_MBDRC_THRESH_2ND_MASK (0xff << TEGRA210_MBDRC_THRESH_2ND_SHIFT)
#define TEGRA210_MBDRC_THRESH_1ST_SHIFT 0
#define TEGRA210_MBDRC_THRESH_1ST_MASK (0xff << TEGRA210_MBDRC_THRESH_1ST_SHIFT)
/* Fields for TEGRA210_MBDRC_RATIO_1ST */
#define TEGRA210_MBDRC_RATIO_1ST_SHIFT 0
#define TEGRA210_MBDRC_RATIO_1ST_MASK (0xffff << TEGRA210_MBDRC_RATIO_1ST_SHIFT)
/* Fields for TEGRA210_MBDRC_RATIO_2ND */
#define TEGRA210_MBDRC_RATIO_2ND_SHIFT 0
#define TEGRA210_MBDRC_RATIO_2ND_MASK (0xffff << TEGRA210_MBDRC_RATIO_2ND_SHIFT)
/* Fields for TEGRA210_MBDRC_RATIO_3RD */
#define TEGRA210_MBDRC_RATIO_3RD_SHIFT 0
#define TEGRA210_MBDRC_RATIO_3RD_MASK (0xffff << TEGRA210_MBDRC_RATIO_3RD_SHIFT)
/* Fields for TEGRA210_MBDRC_RATIO_4TH */
#define TEGRA210_MBDRC_RATIO_4TH_SHIFT 0
#define TEGRA210_MBDRC_RATIO_4TH_MASK (0xffff << TEGRA210_MBDRC_RATIO_4TH_SHIFT)
/* Fields for TEGRA210_MBDRC_RATIO_5TH */
#define TEGRA210_MBDRC_RATIO_5TH_SHIFT 0
#define TEGRA210_MBDRC_RATIO_5TH_MASK (0xffff << TEGRA210_MBDRC_RATIO_5TH_SHIFT)
/* Fields for TEGRA210_MBDRC_MAKEUP_GAIN */
#define TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT 0
#define TEGRA210_MBDRC_MAKEUP_GAIN_MASK (0x3f << TEGRA210_MBDRC_MAKEUP_GAIN_SHIFT)
/* Fields for TEGRA210_MBDRC_INIT_GAIN */
#define TEGRA210_MBDRC_INIT_GAIN_SHIFT 0
#define TEGRA210_MBDRC_INIT_GAIN_MASK (0xffffffff << TEGRA210_MBDRC_INIT_GAIN_SHIFT)
/* Fields for TEGRA210_MBDRC_GAIN_ATTACK */
#define TEGRA210_MBDRC_GAIN_ATTACK_SHIFT 0
#define TEGRA210_MBDRC_GAIN_ATTACK_MASK (0xffffffff << TEGRA210_MBDRC_GAIN_ATTACK_SHIFT)
/* Fields for TEGRA210_MBDRC_GAIN_RELEASE */
#define TEGRA210_MBDRC_GAIN_RELEASE_SHIFT 0
#define TEGRA210_MBDRC_GAIN_RELEASE_MASK (0xffffffff << TEGRA210_MBDRC_GAIN_RELEASE_SHIFT)
/* Fields for TEGRA210_MBDRC_FAST_RELEASE */
#define TEGRA210_MBDRC_FAST_RELEASE_SHIFT 0
#define TEGRA210_MBDRC_FAST_RELEASE_MASK (0xffffffff << TEGRA210_MBDRC_FAST_RELEASE_SHIFT)
/* Fields in TEGRA210_mbdrc ram ctrl */
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT 31
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_READ_BUSY_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_SHIFT 16
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_MASK (0xff << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_READ_COUNT_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT 14
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_READ (0 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_WRITE (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_MASK (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT 0
#define TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_MASK (0x1ff << TEGRA210_MBDRC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT)
/* MBDRC register definition ends here */
/* order and size of each structure element for following structures should not
be altered size order of elements and their size are based on PEQ
co-eff ram and shift ram layout.
*/
#define TEGRA210_MBDRC_THRESHOLD_NUM 4
#define TEGRA210_MBDRC_RATIO_NUM (TEGRA210_MBDRC_THRESHOLD_NUM + 1)
#define TEGRA210_MBDRC_MAX_BIQUAD_STAGES 8
#define TEGRA210_MBDRC_BIQ_PARAMS_PER_STAGE 5
struct tegra210_mbdrc_band_params {
u32 band;
u32 iir_stages;
u32 in_attack_tc;
u32 in_release_tc;
u32 fast_attack_tc;
u32 in_threshold[TEGRA210_MBDRC_THRESHOLD_NUM];
u32 out_threshold[TEGRA210_MBDRC_THRESHOLD_NUM];
u32 ratio[TEGRA210_MBDRC_RATIO_NUM];
u32 makeup_gain;
u32 gain_init;
u32 gain_attack_tc;
u32 gain_release_tc;
u32 fast_release_tc;
/* For biquad_params[][5] order of coeff is b0, b1, a0, a1, a2 */
u32 biquad_params[TEGRA210_MBDRC_MAX_BIQUAD_STAGES * 5];
};
struct tegra210_mbdrc_config {
unsigned int mode;
unsigned int rms_off;
unsigned int peak_rms_mode;
unsigned int fliter_structure;
unsigned int shift_ctrl;
unsigned int frame_size;
unsigned int channel_mask;
unsigned int fa_factor; /* Fast attack factor */
unsigned int fr_factor; /* Fast release factor */
struct tegra210_mbdrc_band_params band_params[MBDRC_NUM_BAND];
};
#endif

View File

@@ -0,0 +1,715 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mixer.c - Tegra210 MIXER driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#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 <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_mixer.h"
#define MIXER_RX_REG(reg, id) (reg + (id * TEGRA210_MIXER_RX_STRIDE))
#define MIXER_TX_REG(reg, id) (reg + (id * TEGRA210_MIXER_TX_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_RX_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \
{ MIXER_RX_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \
{ MIXER_RX_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0}
#define MIXER_TX_REG_DEFAULTS(id) \
{ MIXER_TX_REG(TEGRA210_MIXER_TX1_INT_MASK, id), 0x00000001}, \
{ MIXER_TX_REG(TEGRA210_MIXER_TX1_CIF_CTRL, id), 0x00007700}
static const struct reg_default tegra210_mixer_reg_defaults[] = {
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),
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},
};
static int 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 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_put_format(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);
int value = ucontrol->value.integer.value[0];
if (strstr(kcontrol->id.name, "Audio Channels")) {
if (value >= 0 && value <= 8)
mixer->channels_via_control[mc->reg - 1] = value;
else
return -EINVAL;
}
return 0;
}
static int tegra210_mixer_get_format(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);
if (strstr(kcontrol->id.name, "Audio Channels"))
ucontrol->value.integer.value[0] =
mixer->channels_via_control[mc->reg - 1];
return 0;
}
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, i;
int err;
pm_runtime_get_sync(cmpnt->dev);
/* write default gain config poly coefficients */
for (i = 0; i < 10; i++)
tegra210_mixer_write_ram(mixer, reg + i, mixer->gain_coeff[i]);
/* set duration parameter */
if (strstr(kcontrol->id.name, "Instant")) {
for (; i < 14; i++)
tegra210_mixer_write_ram(mixer, reg + i, 1);
} else {
for (; i < 14; i++)
tegra210_mixer_write_ram(mixer, reg + i,
mixer->gain_coeff[i]);
}
/* write new gain and trigger config */
err = tegra210_mixer_write_ram(mixer, reg + 0x09,
ucontrol->value.integer.value[0]);
err |= tegra210_mixer_write_ram(mixer, reg + 0x0f,
ucontrol->value.integer.value[0]);
pm_runtime_put(cmpnt->dev);
/* save gain */
i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
mixer->gain_value[i] = ucontrol->value.integer.value[0];
return err;
}
static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
struct snd_pcm_hw_params *params,
unsigned int reg,
unsigned int id)
{
int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
if (mixer->channels_via_control[id])
channels = mixer->channels_via_control[id];
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
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, &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, i;
err = tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_RX1_CIF_CTRL +
(dai->id * TEGRA210_MIXER_RX_STRIDE),
dai->id);
/* write the gain config poly coefficients */
for (i = 0; i < 14; i++) {
tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + i,
mixer->gain_coeff[i]);
}
/* write saved gain */
err = tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0x09,
mixer->gain_value[dai->id]);
/* trigger the polynomial configuration */
tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0xf,
0x01);
return err;
}
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);
int err;
err = tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_TX1_CIF_CTRL +
((dai->id-10) * TEGRA210_MIXER_TX_STRIDE),
dai->id);
return err;
}
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(sname, id, dai_ops) \
{ \
.name = #sname #id, \
.playback = { \
.stream_name = #sname #id " Receive", \
.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_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = dai_ops, \
}
#define OUT_DAI(sname, id, dai_ops) \
{ \
.name = #sname #id, \
.capture = { \
.stream_name = #sname #id " Transmit", \
.channels_min = 1, \
.channels_max = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE, \
}, \
.ops = dai_ops, \
}
static struct snd_soc_dai_driver tegra210_mixer_dais[] = {
IN_DAI(RX, 1, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 2, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 3, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 4, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 5, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 6, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 7, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 8, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 9, &tegra210_mixer_in_dai_ops),
IN_DAI(RX, 10, &tegra210_mixer_in_dai_ops),
OUT_DAI(TX, 1, &tegra210_mixer_out_dai_ops),
OUT_DAI(TX, 2, &tegra210_mixer_out_dai_ops),
OUT_DAI(TX, 3, &tegra210_mixer_out_dai_ops),
OUT_DAI(TX, 4, &tegra210_mixer_out_dai_ops),
OUT_DAI(TX, 5, &tegra210_mixer_out_dai_ops),
};
#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);
static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { \
SOC_SINGLE_EXT("RX1 Gain", MIXER_GAIN_CFG_RAM_ADDR(0), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX2 Gain", MIXER_GAIN_CFG_RAM_ADDR(1), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX3 Gain", MIXER_GAIN_CFG_RAM_ADDR(2), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX4 Gain", MIXER_GAIN_CFG_RAM_ADDR(3), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX5 Gain", MIXER_GAIN_CFG_RAM_ADDR(4), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX6 Gain", MIXER_GAIN_CFG_RAM_ADDR(5), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX7 Gain", MIXER_GAIN_CFG_RAM_ADDR(6), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX8 Gain", MIXER_GAIN_CFG_RAM_ADDR(7), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX9 Gain", MIXER_GAIN_CFG_RAM_ADDR(8), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX10 Gain", MIXER_GAIN_CFG_RAM_ADDR(9), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX1 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(0), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX2 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(1), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX3 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(2), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX4 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(3), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX5 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(4), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX6 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(5), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX7 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(6), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX8 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(7), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX9 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(8), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX10 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(9), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX1 Audio Channels", 1, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX2 Audio Channels", 2, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX3 Audio Channels", 3, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX4 Audio Channels", 4, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX5 Audio Channels", 5, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX6 Audio Channels", 6, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX7 Audio Channels", 7, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX8 Audio Channels", 8, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX9 Audio Channels", 9, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX10 Audio Channels", 10, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX1 Audio Channels", 11, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX2 Audio Channels", 12, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX3 Audio Channels", 13, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX4 Audio Channels", 14, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX5 Audio Channels", 15, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE("Mixer Enable", TEGRA210_MIXER_ENABLE, 0, 1, 0),
};
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 MIXER_ROUTES(name, id) \
{name, "RX1", "RX1",}, \
{name, "RX2", "RX2",}, \
{name, "RX3", "RX3",}, \
{name, "RX4", "RX4",}, \
{name, "RX5", "RX5",}, \
{name, "RX6", "RX6",}, \
{name, "RX7", "RX7",}, \
{name, "RX8", "RX8",}, \
{name, "RX9", "RX9",}, \
{name, "RX10", "RX10"}, \
{"TX"#id, NULL, name}
static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {
{ "RX1", NULL, "RX1 Receive" },
{ "RX2", NULL, "RX2 Receive" },
{ "RX3", NULL, "RX3 Receive" },
{ "RX4", NULL, "RX4 Receive" },
{ "RX5", NULL, "RX5 Receive" },
{ "RX6", NULL, "RX6 Receive" },
{ "RX7", NULL, "RX7 Receive" },
{ "RX8", NULL, "RX8 Receive" },
{ "RX9", NULL, "RX9 Receive" },
{ "RX10", NULL, "RX10 Receive" },
/* route between MIXER RXs and TXs */
MIXER_ROUTES("Adder1", 1),
MIXER_ROUTES("Adder2", 2),
MIXER_ROUTES("Adder3", 3),
MIXER_ROUTES("Adder4", 4),
MIXER_ROUTES("Adder5", 5),
{ "TX1 Transmit", NULL, "TX1" },
{ "TX2 Transmit", NULL, "TX2" },
{ "TX3 Transmit", NULL, "TX3" },
{ "TX4 Transmit", NULL, "TX4" },
{ "TX5 Transmit", NULL, "TX5" },
};
static 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),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra210_mixer_wr_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
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 %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
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 %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
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;
mixer->gain_coeff[0] = 0;
mixer->gain_coeff[1] = 0;
mixer->gain_coeff[2] = 0;
mixer->gain_coeff[3] = 0;
mixer->gain_coeff[4] = 0;
mixer->gain_coeff[5] = 0;
mixer->gain_coeff[6] = 0;
mixer->gain_coeff[7] = 0x1000000;
mixer->gain_coeff[8] = 0;
mixer->gain_coeff[9] = 0x10000;
mixer->gain_coeff[10] = 0;
mixer->gain_coeff[11] = 0;
mixer->gain_coeff[12] = 0x400;
mixer->gain_coeff[13] = 0x8000000;
dev_set_drvdata(dev, mixer);
for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
mixer->gain_value[i] = 0x10000;
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,112 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_mixer.h - Definitions for Tegra210 MIXER driver
*
* Copyright (c) 2015-2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_MIXER_H__
#define __TEGRA210_MIXER_H__
#define TEGRA210_MIXER_RX_STRIDE 0x40
#define TEGRA210_MIXER_RX_MAX 10
#define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_RX_STRIDE)
/* AXBAR_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
#define TEGRA210_MIXER_RX1_CYA 0x34
#define TEGRA210_MIXER_RX1_DBG0 0x38
#define TEGRA210_MIXER_RX1_DBG1 0x3c
#define TEGRA210_MIXER_TX_STRIDE 0x40
#define TEGRA210_MIXER_TX_MAX 5
#define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_TX_STRIDE))
/* AXBAR_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
#define TEGRA210_MIXER_TX1_CYA 0x2ac
#define TEGRA210_MIXER_TX1_DBG0 0x2b0
#define TEGRA210_MIXER_TX1_DBG1 0x2b4
/* 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_CYA 0x440
#define TEGRA210_MIXER_DBG 0x448
#define TEGRA210_MIXER_TX_ENABLE_SHIFT 0
#define TEGRA210_MIXER_TX_EN (1 << TEGRA210_MIXER_TX_ENABLE_SHIFT)
/* TODO Add fields for MIXER_TX1_ADDER_CONFIG register */
#define TEGRA210_MIXER_TX2_ADDER_CONFIG (TEGRA210_MIXER_TX1_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX3_ADDER_CONFIG (TEGRA210_MIXER_TX2_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX4_ADDER_CONFIG (TEGRA210_MIXER_TX3_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX5_ADDER_CONFIG (TEGRA210_MIXER_TX4_ADDER_CONFIG + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX2_ENABLE (TEGRA210_MIXER_TX1_ENABLE + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX3_ENABLE (TEGRA210_MIXER_TX2_ENABLE + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX4_ENABLE (TEGRA210_MIXER_TX3_ENABLE + TEGRA210_MIXER_TX_STRIDE)
#define TEGRA210_MIXER_TX5_ENABLE (TEGRA210_MIXER_TX4_ENABLE + TEGRA210_MIXER_TX_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_CTRL */
#define TEGRA210_MIXER_CTRL_ENABLE_BYPASS_MODE 1
#define TEGRA210_MIXER_CTRL_DISABLE_BYPASS_MODE 0
/* 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_TOTAL_PATH (TEGRA210_MIXER_RX_MAX + TEGRA210_MIXER_TX_MAX)
struct tegra210_mixer {
struct regmap *regmap;
int gain_coeff[14];
int gain_value[TEGRA210_MIXER_RX_MAX];
unsigned int channels_via_control[TEGRA210_MIXER_TOTAL_PATH];
};
#endif

View File

@@ -0,0 +1,779 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mvc.c - Tegra210 MVC driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#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 <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_mvc.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 int 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 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 int tegra210_mvc_write_ram(struct tegra210_mvc *mvc,
unsigned int addr, unsigned int coef)
{
unsigned int reg, val;
int err;
err = regmap_read_poll_timeout(mvc->regmap,
TEGRA210_MVC_CFG_RAM_CTRL,
val, !(val & 0x80000000), 10, 10000);
if (err < 0)
return err;
reg = (addr << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT) &
TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK;
reg |= TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN;
reg |= TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE;
reg |= TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN;
regmap_write(mvc->regmap, TEGRA210_MVC_CFG_RAM_CTRL, reg);
regmap_write(mvc->regmap, TEGRA210_MVC_CFG_RAM_DATA,
coef);
return 0;
}
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);
unsigned int reg = mc->reg;
if (reg == TEGRA210_MVC_CTRL) {
u32 val;
u8 mute_mask;
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;
if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) {
/*
* If per channel control is enabled, then return
* exact mute/unmute setting of all channels.
*
* Else report setting based on CH0 bit to reflect
* the correct HW state.
*/
if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
ucontrol->value.integer.value[0] = mute_mask;
} else {
if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
ucontrol->value.integer.value[0] =
TEGRA210_MUTE_MASK_EN;
else
ucontrol->value.integer.value[0] = 0;
}
} else {
/*
* If per channel control is disabled, then return
* master mute/unmute setting based on CH0 bit.
*
* Else report settings based on state of all
* channels.
*/
if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
ucontrol->value.integer.value[0] =
mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
} else {
if (mute_mask == TEGRA210_MUTE_MASK_EN)
ucontrol->value.integer.value[0] =
TEGRA210_MVC_CH0_MUTE_EN;
else
ucontrol->value.integer.value[0] = 0;
}
}
} else {
u8 chan = (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 void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
{
/* Volume control read from mixer ctl 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_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;
int err, i;
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;
if (reg == TEGRA210_MVC_CTRL) {
u32 reg_mask;
u8 mute_mask;
mute_mask = ucontrol->value.integer.value[0];
if (strstr(kcontrol->id.name, "Per Chan Mute Mask")) {
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
TEGRA210_MVC_PER_CHAN_CTRL_EN);
reg_mask = TEGRA210_MVC_MUTE_MASK;
} else {
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
0);
reg_mask = TEGRA210_MVC_CH0_MUTE_MASK;
}
regmap_update_bits(mvc->regmap, reg, reg_mask,
mute_mask << TEGRA210_MVC_MUTE_SHIFT);
} else {
u8 chan;
chan = (reg - TEGRA210_MVC_TARGET_VOL)/REG_SIZE;
tegra210_mvc_conv_vol(mvc, chan,
ucontrol->value.integer.value[0]);
/* Config init vol same as target vol */
if (strstr(kcontrol->id.name, "Channel")) {
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
TEGRA210_MVC_PER_CHAN_CTRL_EN);
regmap_write(mvc->regmap,
TEGRA210_MVC_REG_OFFSET(
TEGRA210_MVC_INIT_VOL, chan),
mvc->volume[chan]);
regmap_write(mvc->regmap, reg, mvc->volume[chan]);
} else {
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
0);
for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
mvc->volume[i] = mvc->volume[0];
regmap_write(mvc->regmap, TEGRA210_MVC_INIT_VOL,
mvc->volume[0]);
regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL,
mvc->volume[0]);
}
}
regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
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 the volume for channels in MVC */
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 0;
}
static int tegra210_mvc_get_audio_bits(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);
if (mvc->audio_bits > 0)
ucontrol->value.integer.value[0] = (mvc->audio_bits + 1) * 4;
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int tegra210_mvc_put_audio_bits(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);
unsigned int val;
val = ucontrol->value.integer.value[0];
if ((val >= 8) && (val <= 32) && (val%4 == 0))
mvc->audio_bits = val/4 - 1;
else if (val == 0)
mvc->audio_bits = 0;
else
return -EINVAL;
return 0;
}
static int tegra210_mvc_get_format(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);
/* get the format control flag */
if (strstr(kcontrol->id.name, "Audio Bit Format"))
ucontrol->value.integer.value[0] = mvc->format_in;
else if (strstr(kcontrol->id.name, "Audio Channels"))
ucontrol->value.integer.value[0] = mvc->cif_channels;
else if (strstr(kcontrol->id.name, "Bypass"))
ucontrol->value.integer.value[0] = mvc->bypass_mode;
return 0;
}
static int tegra210_mvc_put_format(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);
unsigned int value = ucontrol->value.integer.value[0];
/* set the format control flag */
if (strstr(kcontrol->id.name, "Audio Bit Format"))
mvc->format_in = value;
else if (strstr(kcontrol->id.name, "Audio Channels"))
mvc->cif_channels = value;
else if (strstr(kcontrol->id.name, "Bypass"))
mvc->bypass_mode = value;
return 0;
}
static const int tegra210_mvc_fmt_values[] = {
0,
TEGRA_ACIF_BITS_16,
TEGRA_ACIF_BITS_32,
};
static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
struct snd_pcm_hw_params *params,
unsigned int reg)
{
int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
if (mvc->cif_channels > 0)
channels = mvc->cif_channels;
if (channels > 8)
return -EINVAL;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
if (mvc->audio_bits > 0)
audio_bits = mvc->audio_bits;
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
/* Override input format to MVC, if set */
if (mvc->format_in && (reg == TEGRA210_MVC_RX_CIF_CTRL))
cif_conf.audio_bits = tegra210_mvc_fmt_values[mvc->format_in];
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 i, err, val;
/* SW reset */
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 and TX 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;
}
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;
}
/* program the poly coefficients */
for (i = 0; i < 9; i++) {
err = tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]);
if (err < 0) {
dev_err(dai->dev, "failed to write coefs, err = %d\n",
err);
return err;
}
}
/* program poly_n1, poly_n2, duration */
regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, mvc->poly_n1);
regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, mvc->poly_n2);
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, mvc->duration);
/* program duration_inv */
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, mvc->duration_inv);
/* set bypass mode */
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
TEGRA210_MVC_BYPASS_MODE_MASK,
mvc->bypass_mode << TEGRA210_MVC_BYPASS_MODE_SHIFT);
return err;
}
static struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
.hw_params = tegra210_mvc_hw_params,
};
static const unsigned int tegra210_mvc_curve_type_values[] = {
CURVE_POLY,
CURVE_LINEAR,
};
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);
static const char * const tegra210_mvc_format_text[] = {
"None",
"16",
"32",
};
static const struct soc_enum tegra210_mvc_format_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
ARRAY_SIZE(tegra210_mvc_format_text),
tegra210_mvc_format_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[] = {
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),
SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
tegra210_mvc_get_vol, tegra210_mvc_put_vol),
SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
tegra210_mvc_get_vol, tegra210_mvc_put_vol),
SOC_SINGLE_EXT("Per Chan Mute Mask", TEGRA210_MVC_CTRL, 0,
TEGRA210_MUTE_MASK_EN, 0, tegra210_mvc_get_vol,
tegra210_mvc_put_vol),
SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
SOC_SINGLE_EXT("Bits", 0, 0, 32, 0,
tegra210_mvc_get_audio_bits, tegra210_mvc_put_audio_bits),
SOC_SINGLE_EXT("Audio Channels", 0, 0, 8, 0,
tegra210_mvc_get_format, tegra210_mvc_put_format),
SOC_ENUM_EXT("Audio Bit Format", tegra210_mvc_format_enum,
tegra210_mvc_get_format, tegra210_mvc_put_format),
SOC_SINGLE_EXT("Bypass", TEGRA210_MVC_CTRL, 0, 1, 0,
tegra210_mvc_get_format, tegra210_mvc_put_format),
};
static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
{
.name = "MVC IN",
.playback = {
.stream_name = "MVC Receive",
.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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "MVC OUT",
.capture = {
.stream_name = "MVC Transmit",
.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_S24_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("MVC RX", NULL, 0, SND_SOC_NOPM,
0, 0),
SND_SOC_DAPM_AIF_OUT("MVC TX", NULL, 0, TEGRA210_MVC_ENABLE,
TEGRA210_MVC_EN_SHIFT, 0),
};
static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
{ "MVC RX", NULL, "MVC Receive" },
{ "MVC TX", NULL, "MVC RX" },
{ "MVC Transmit", NULL, "MVC TX" },
};
static 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),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_DBG:
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_CYA:
case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CYA:
case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG:
case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA:
case TEGRA210_MVC_CYA:
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_CYA,
.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->poly_n1 = 16;
mvc->poly_n2 = 63;
mvc->duration = 150;
mvc->duration_inv = 14316558;
mvc->poly_coeff[0] = 23738319;
mvc->poly_coeff[1] = 659403;
mvc->poly_coeff[2] = -3680;
mvc->poly_coeff[3] = 15546680;
mvc->poly_coeff[4] = 2530732;
mvc->poly_coeff[5] = -120985;
mvc->poly_coeff[6] = 12048422;
mvc->poly_coeff[7] = 5527252;
mvc->poly_coeff[8] = -785042;
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");

View File

@@ -0,0 +1,124 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_mvc.h - Definitions for Tegra210 MVC driver
*
* Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_MVC_H__
#define __TEGRA210_MVC_H__
/*
* MVC_RX registers are with respect to AXBAR.
* The data is coming from AXBAR to MVC for playback.
*/
#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
#define TEGRA210_MVC_RX_CYA 0x24
#define TEGRA210_MVC_RX_DBG 0x28
/*
* MVC_TX registers are with respect to AXBAR.
* The data is going out of MVC for playback.
*/
#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
#define TEGRA210_MVC_TX_CYA 0x64
#define TEGRA210_MVC_TX_DBG 0x68
/* 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
#define TEGRA210_MVC_CYA 0x130
#define TEGRA210_MVC_DBG 0x138
/* 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_CH0_MUTE_EN 1
#define TEGRA210_MVC_CH0_MUTE_MASK (TEGRA210_MVC_CH0_MUTE_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
#define TEGRA210_MVC_BYPASS_MODE_SHIFT 31
#define TEGRA210_MVC_BYPASS_MODE_MASK (1 << TEGRA210_MVC_BYPASS_MODE_SHIFT)
/* 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))
enum {
CURVE_POLY,
CURVE_LINEAR,
};
struct tegra210_mvc {
struct regmap *regmap;
int poly_coeff[9];
int poly_n1, poly_n2, duration, duration_inv;
int volume[TEGRA210_MVC_MAX_CHAN_COUNT];
unsigned int curve_type;
unsigned int ctrl_value;
unsigned int cif_channels;
unsigned int audio_bits;
unsigned int format_in;
bool bypass_mode;
};
#endif

View File

@@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_ope.c - Tegra210 OPE driver
//
// Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#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 <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_ope.h"
static const struct reg_default tegra210_ope_reg_defaults[] = {
{ TEGRA210_OPE_AXBAR_RX_INT_MASK, 0x00000001},
{ TEGRA210_OPE_AXBAR_RX_CIF_CTRL, 0x00007700},
{ TEGRA210_OPE_AXBAR_TX_INT_MASK, 0x00000001},
{ TEGRA210_OPE_AXBAR_TX_CIF_CTRL, 0x00007700},
{ TEGRA210_OPE_CG, 0x1},
};
static int tegra210_ope_runtime_suspend(struct device *dev)
{
struct tegra210_ope *ope = dev_get_drvdata(dev);
tegra210_peq_save(ope);
regcache_cache_only(ope->mbdrc_regmap, true);
regcache_cache_only(ope->peq_regmap, true);
regcache_cache_only(ope->regmap, true);
regcache_mark_dirty(ope->regmap);
regcache_mark_dirty(ope->peq_regmap);
regcache_mark_dirty(ope->mbdrc_regmap);
return 0;
}
static int tegra210_ope_runtime_resume(struct device *dev)
{
struct tegra210_ope *ope = dev_get_drvdata(dev);
regcache_cache_only(ope->regmap, false);
regcache_cache_only(ope->peq_regmap, false);
regcache_cache_only(ope->mbdrc_regmap, false);
regcache_sync(ope->regmap);
regcache_sync(ope->peq_regmap);
regcache_sync(ope->mbdrc_regmap);
tegra210_peq_restore(ope);
return 0;
}
static int tegra210_ope_set_audio_cif(struct tegra210_ope *ope,
struct snd_pcm_hw_params *params,
unsigned int reg)
{
int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
if (channels < 2)
return -EINVAL;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
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(ope->regmap, reg, &cif_conf);
return 0;
}
static int tegra210_ope_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_ope *ope = snd_soc_dai_get_drvdata(dai);
int err;
/* set RX cif and TX cif */
err = tegra210_ope_set_audio_cif(ope, params,
TEGRA210_OPE_AXBAR_RX_CIF_CTRL);
if (err) {
dev_err(dev, "Can't set OPE RX CIF: %d\n", err);
return err;
}
err = tegra210_ope_set_audio_cif(ope, params,
TEGRA210_OPE_AXBAR_TX_CIF_CTRL);
if (err) {
dev_err(dev, "Can't set OPE TX CIF: %d\n", err);
return err;
}
tegra210_mbdrc_hw_params(dai->component);
return err;
}
static int tegra210_ope_codec_probe(struct snd_soc_component *cmpnt)
{
struct tegra210_ope *ope = dev_get_drvdata(cmpnt->dev);
tegra210_peq_codec_init(cmpnt);
tegra210_mbdrc_codec_init(cmpnt);
/*
* The OPE, PEQ and MBDRC functionalities are combined under one
* device registered by OPE driver. However there are separate
* regmap interfaces for each of these. ASoC core depends on
* dev_get_regmap() to populate the regmap field for a given ASoC
* component. Due to multiple regmap interfaces, it always uses
* the last registered interface in probe(). The DAPM routes in
* the current driver depend on OPE regmap. So to avoid dependency
* on probe order and to allow DAPM paths to use correct regmap
* below explicit assignment is done.
*/
snd_soc_component_init_regmap(cmpnt, ope->regmap);
return 0;
}
static struct snd_soc_dai_ops tegra210_ope_dai_ops = {
.hw_params = tegra210_ope_hw_params,
};
static struct snd_soc_dai_driver tegra210_ope_dais[] = {
{
.name = "OPE IN",
.playback = {
.stream_name = "OPE Receive",
.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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
},
{
.name = "OPE OUT",
.capture = {
.stream_name = "OPE Transmit",
.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_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &tegra210_ope_dai_ops,
}
};
static const struct snd_soc_dapm_widget tegra210_ope_widgets[] = {
SND_SOC_DAPM_AIF_IN("OPE RX", NULL, 0, SND_SOC_NOPM,
0, 0),
SND_SOC_DAPM_AIF_OUT("OPE TX", NULL, 0, TEGRA210_OPE_ENABLE,
TEGRA210_OPE_EN_SHIFT, 0),
};
static const struct snd_soc_dapm_route tegra210_ope_routes[] = {
{ "OPE RX", NULL, "OPE Receive" },
{ "OPE TX", NULL, "OPE RX" },
{ "OPE Transmit", NULL, "OPE TX" },
};
static const struct snd_kcontrol_new tegra210_ope_controls[] = {
SOC_SINGLE("direction peq to mbdrc", TEGRA210_OPE_DIRECTION,
TEGRA210_OPE_DIRECTION_SHIFT, 1, 0),
};
static struct snd_soc_component_driver tegra210_ope_cmpnt = {
.probe = tegra210_ope_codec_probe,
.dapm_widgets = tegra210_ope_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_ope_widgets),
.dapm_routes = tegra210_ope_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_ope_routes),
.controls = tegra210_ope_controls,
.num_controls = ARRAY_SIZE(tegra210_ope_controls),
#if defined(NV_SND_SOC_COMPONENT_DRIVER_STRUCT_HAS_NON_LEGACY_DAI_NAMING) /* Linux v6.0 */
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra210_ope_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_OPE_AXBAR_RX_INT_MASK:
case TEGRA210_OPE_AXBAR_RX_INT_SET:
case TEGRA210_OPE_AXBAR_RX_INT_CLEAR:
case TEGRA210_OPE_AXBAR_RX_CIF_CTRL:
case TEGRA210_OPE_AXBAR_TX_INT_MASK:
case TEGRA210_OPE_AXBAR_TX_INT_SET:
case TEGRA210_OPE_AXBAR_TX_INT_CLEAR:
case TEGRA210_OPE_AXBAR_TX_CIF_CTRL:
case TEGRA210_OPE_ENABLE:
case TEGRA210_OPE_SOFT_RESET:
case TEGRA210_OPE_CG:
case TEGRA210_OPE_DIRECTION:
return true;
default:
return false;
}
}
static bool tegra210_ope_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_OPE_AXBAR_RX_STATUS:
case TEGRA210_OPE_AXBAR_RX_INT_STATUS:
case TEGRA210_OPE_AXBAR_RX_INT_MASK:
case TEGRA210_OPE_AXBAR_RX_INT_SET:
case TEGRA210_OPE_AXBAR_RX_INT_CLEAR:
case TEGRA210_OPE_AXBAR_RX_CIF_CTRL:
case TEGRA210_OPE_AXBAR_TX_STATUS:
case TEGRA210_OPE_AXBAR_TX_INT_STATUS:
case TEGRA210_OPE_AXBAR_TX_INT_MASK:
case TEGRA210_OPE_AXBAR_TX_INT_SET:
case TEGRA210_OPE_AXBAR_TX_INT_CLEAR:
case TEGRA210_OPE_AXBAR_TX_CIF_CTRL:
case TEGRA210_OPE_ENABLE:
case TEGRA210_OPE_SOFT_RESET:
case TEGRA210_OPE_CG:
case TEGRA210_OPE_STATUS:
case TEGRA210_OPE_INT_STATUS:
case TEGRA210_OPE_DIRECTION:
return true;
default:
return false;
}
}
static bool tegra210_ope_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_OPE_AXBAR_RX_STATUS:
case TEGRA210_OPE_AXBAR_RX_INT_SET:
case TEGRA210_OPE_AXBAR_RX_INT_STATUS:
case TEGRA210_OPE_AXBAR_TX_STATUS:
case TEGRA210_OPE_AXBAR_TX_INT_SET:
case TEGRA210_OPE_AXBAR_TX_INT_STATUS:
case TEGRA210_OPE_SOFT_RESET:
case TEGRA210_OPE_STATUS:
case TEGRA210_OPE_INT_STATUS:
return true;
default:
return false;
}
}
static const struct regmap_config tegra210_ope_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_OPE_DIRECTION,
.writeable_reg = tegra210_ope_wr_reg,
.readable_reg = tegra210_ope_rd_reg,
.volatile_reg = tegra210_ope_volatile_reg,
.reg_defaults = tegra210_ope_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra210_ope_of_match[] = {
{ .compatible = "nvidia,tegra210-ope" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_ope_of_match);
static int tegra210_ope_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_ope *ope;
void __iomem *regs;
int err;
ope = devm_kzalloc(dev, sizeof(*ope), GFP_KERNEL);
if (!ope)
return -ENOMEM;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
ope->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_ope_regmap_config);
if (IS_ERR(ope->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(ope->regmap);
}
regcache_cache_only(ope->regmap, true);
dev_set_drvdata(dev, ope);
err = tegra210_peq_regmap_init(pdev);
if (err < 0) {
dev_err(dev, "PEQ init failed\n");
return err;
}
err = tegra210_mbdrc_regmap_init(pdev);
if (err < 0) {
dev_err(dev, "MBDRC init failed\n");
return err;
}
err = devm_snd_soc_register_component(dev, &tegra210_ope_cmpnt,
tegra210_ope_dais,
ARRAY_SIZE(tegra210_ope_dais));
if (err) {
dev_err(dev, "can't register OPE component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_ope_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct dev_pm_ops tegra210_ope_pm_ops = {
SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend,
tegra210_ope_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_ope_driver = {
.driver = {
.name = "tegra210-ope",
.of_match_table = tegra210_ope_of_match,
.pm = &tegra210_ope_pm_ops,
},
.probe = tegra210_ope_platform_probe,
.remove = tegra210_ope_platform_remove,
};
module_platform_driver(tegra210_ope_driver)
MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 OPE ASoC driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_ope.h - Definitions for Tegra210 OPE driver
*
* Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_OPE_H__
#define __TEGRA210_OPE_H__
#include "tegra210_peq.h"
/* Register offsets from TEGRA210_OPE*_BASE */
/*
* OPE_AXBAR_RX registers are with respect to AXBAR.
* The data is coming from AXBAR to OPE for playback.
*/
#define TEGRA210_OPE_AXBAR_RX_STATUS 0xc
#define TEGRA210_OPE_AXBAR_RX_INT_STATUS 0x10
#define TEGRA210_OPE_AXBAR_RX_INT_MASK 0x14
#define TEGRA210_OPE_AXBAR_RX_INT_SET 0x18
#define TEGRA210_OPE_AXBAR_RX_INT_CLEAR 0x1c
#define TEGRA210_OPE_AXBAR_RX_CIF_CTRL 0x20
/*
* OPE_AXBAR_TX registers are with respect to AXBAR.
* The data is going out of OPE for playback.
*/
#define TEGRA210_OPE_AXBAR_TX_STATUS 0x4c
#define TEGRA210_OPE_AXBAR_TX_INT_STATUS 0x50
#define TEGRA210_OPE_AXBAR_TX_INT_MASK 0x54
#define TEGRA210_OPE_AXBAR_TX_INT_SET 0x58
#define TEGRA210_OPE_AXBAR_TX_INT_CLEAR 0x5c
#define TEGRA210_OPE_AXBAR_TX_CIF_CTRL 0x60
/* OPE Gloabal registers */
#define TEGRA210_OPE_ENABLE 0x80
#define TEGRA210_OPE_SOFT_RESET 0x84
#define TEGRA210_OPE_CG 0x88
#define TEGRA210_OPE_STATUS 0x8c
#define TEGRA210_OPE_INT_STATUS 0x90
#define TEGRA210_OPE_DIRECTION 0x94
/* Fields for TEGRA210_OPE_ENABLE */
#define TEGRA210_OPE_EN_SHIFT 0
#define TEGRA210_OPE_EN (1 << TEGRA210_OPE_EN_SHIFT)
/* Fields for TEGRA210_OPE_SOFT_RESET */
#define TEGRA210_OPE_SOFT_RESET_SHIFT 0
#define TEGRA210_OPE_SOFT_RESET_EN (1 << TEGRA210_OPE_SOFT_RESET_SHIFT)
/* Fields for TEGRA210_OPE_DIRECTION */
#define TEGRA210_OPE_DIRECTION_SHIFT 0
#define TEGRA210_OPE_DIRECTION_MASK (1 << TEGRA210_OPE_DIRECTION_SHIFT)
#define TEGRA210_OPE_DIRECTION_MBDRC_TO_PEQ (0 << TEGRA210_OPE_DIRECTION_SHIFT)
#define TEGRA210_OPE_DIRECTION_PEQ_TO_MBDRC (1 << TEGRA210_OPE_DIRECTION_SHIFT)
/* OPE register definitions end here */
#define TEGRA210_PEQ_IORESOURCE_MEM 1
#define TEGRA210_MBDRC_IORESOURCE_MEM 2
struct tegra210_ope {
struct regmap *regmap;
struct regmap *peq_regmap;
struct regmap *mbdrc_regmap;
u32 peq_biquad_gains[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH];
u32 peq_biquad_shifts[TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH];
};
extern int tegra210_peq_regmap_init(struct platform_device *pdev);
extern int tegra210_peq_codec_init(struct snd_soc_component *cmpnt);
extern void tegra210_peq_restore(struct tegra210_ope *ope);
extern void tegra210_peq_save(struct tegra210_ope *ope);
extern int tegra210_mbdrc_regmap_init(struct platform_device *pdev);
extern int tegra210_mbdrc_codec_init(struct snd_soc_component *cmpnt);
extern int tegra210_mbdrc_hw_params(struct snd_soc_component *cmpnt);
/* Extension of soc_bytes structure defined in sound/soc.h */
struct tegra_soc_bytes {
struct soc_bytes soc;
u32 shift; /* Used as offset for ahub ram related programing */
};
/* Utility structures for using mixer control of type snd_soc_bytes */
#define TEGRA_SOC_BYTES_EXT(xname, xbase, xregs, xshift, xmask, \
xhandler_get, xhandler_put, xinfo) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.info = xinfo, \
.get = xhandler_get, \
.put = xhandler_put, \
.private_value = ((unsigned long)&(struct tegra_soc_bytes) \
{ \
.soc.base = xbase, \
.soc.num_regs = xregs, \
.soc.mask = xmask, \
.shift = xshift \
}) \
}
#endif

View File

@@ -0,0 +1,397 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_peq.c - Tegra210 PEQ driver
//
// Copyright (c) 2014-2023, 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_address.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_ahub.h"
#include "tegra210_ope.h"
#include "tegra210_peq.h"
static const struct reg_default tegra210_peq_reg_defaults[] = {
{ TEGRA210_PEQ_CONFIG, 0x00000013},
{ TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, 0x00004000},
{ TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, 0x00004000},
};
/* Default PEQ filter parameters for a 5-stage biquad*/
static const int biquad_init_stage = 5;
static const u32 biquad_init_gains[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH] = {
1495012349, /* pre-gain */
/* Gains : b0, b1, a0, a1, a2 */
536870912, -1073741824, 536870912, 2143508246, -1069773768, /* band-0 */
134217728, -265414508, 131766272, 2140402222, -1071252997, /* band-1 */
268435456, -233515765, -33935948, 1839817267, -773826124, /* band-2 */
536870912, -672537913, 139851540, 1886437554, -824433167, /* band-3 */
268435456, -114439279, 173723964, 205743566, 278809729, /* band-4 */
1, 0, 0, 0, 0, /* band-5 */
1, 0, 0, 0, 0, /* band-6 */
1, 0, 0, 0, 0, /* band-7 */
1, 0, 0, 0, 0, /* band-8 */
1, 0, 0, 0, 0, /* band-9 */
1, 0, 0, 0, 0, /* band-10 */
1, 0, 0, 0, 0, /* band-11 */
963423114, /* post-gain */
};
static const u32 biquad_init_shifts[TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH] = {
23, /* pre-shift */
30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, /* shift for bands */
28, /* post-shift */
};
static s32 biquad_coeff_buffer[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH];
static int tegra210_peq_get(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int val;
regmap_read(ope->peq_regmap, mc->reg, &val);
ucontrol->value.integer.value[0] = (val >> mc->shift) & mask;
if (mc->invert)
ucontrol->value.integer.value[0] =
mc->max - ucontrol->value.integer.value[0];
return 0;
}
static int tegra210_peq_put(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_ope *ope = snd_soc_component_get_drvdata(cmpnt);
unsigned int mask = (1 << fls(mc->max)) - 1;
unsigned int val;
val = (ucontrol->value.integer.value[0] & mask);
if (mc->invert)
val = mc->max - val;
val = val << mc->shift;
return regmap_update_bits(ope->peq_regmap, mc->reg,
(mask << mc->shift), val);
}
static int tegra210_peq_ahub_ram_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 i, reg_ctrl = params->soc.base;
u32 reg_data = reg_ctrl + cmpnt->val_bytes;
s32 *data = (s32 *)biquad_coeff_buffer;
pm_runtime_get_sync(cmpnt->dev);
tegra210_ahub_read_ram(ope->peq_regmap, reg_ctrl, reg_data,
params->shift, data, params->soc.num_regs);
pm_runtime_put_sync(cmpnt->dev);
for (i = 0; i < params->soc.num_regs; i++)
ucontrol->value.integer.value[i] = (long)data[i];
return 0;
}
static int tegra210_peq_ahub_ram_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tegra_soc_bytes *params = (void *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
u32 i, reg_ctrl = params->soc.base;
u32 reg_data = reg_ctrl + cmpnt->val_bytes;
s32 *data = (s32 *)biquad_coeff_buffer;
for (i = 0; i < params->soc.num_regs; i++)
data[i] = (s32)ucontrol->value.integer.value[i];
pm_runtime_get_sync(cmpnt->dev);
tegra210_ahub_write_ram(ope->peq_regmap, reg_ctrl, reg_data,
params->shift, data, params->soc.num_regs);
pm_runtime_put_sync(cmpnt->dev);
return 0;
}
static int tegra210_peq_param_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_bytes *params = (void *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = -0x7fffffff;
uinfo->value.integer.max = 0x7fffffff;
uinfo->count = params->num_regs;
return 0;
}
#define TEGRA210_PEQ_GAIN_PARAMS_CTRL(chan) \
TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " biquad gain params", \
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL, \
TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH, \
(TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH * chan), 0xffffffff, \
tegra210_peq_ahub_ram_get, tegra210_peq_ahub_ram_put, \
tegra210_peq_param_info)
#define TEGRA210_PEQ_SHIFT_PARAMS_CTRL(chan) \
TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " biquad shift params", \
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL, \
TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH, \
(TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH * chan), 0x1f, \
tegra210_peq_ahub_ram_get, tegra210_peq_ahub_ram_put, \
tegra210_peq_param_info)
static const struct snd_kcontrol_new tegra210_peq_controls[] = {
SOC_SINGLE_EXT("PEQ Active", TEGRA210_PEQ_CONFIG,
TEGRA210_PEQ_CONFIG_MODE_SHIFT, 1, 0,
tegra210_peq_get, tegra210_peq_put),
SOC_SINGLE_EXT("PEQ Biquad Stages", TEGRA210_PEQ_CONFIG,
TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT,
TEGRA210_PEQ_MAX_BIQUAD_STAGES - 1, 0,
tegra210_peq_get, tegra210_peq_put),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(0),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(1),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(2),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(3),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(4),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(5),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(6),
TEGRA210_PEQ_GAIN_PARAMS_CTRL(7),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(0),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(1),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(2),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(3),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(4),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(5),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(6),
TEGRA210_PEQ_SHIFT_PARAMS_CTRL(7),
};
static bool tegra210_peq_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_PEQ_SOFT_RESET:
case TEGRA210_PEQ_CG:
case TEGRA210_PEQ_CONFIG:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA:
return true;
default:
return false;
}
}
static bool tegra210_peq_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_PEQ_SOFT_RESET:
case TEGRA210_PEQ_CG:
case TEGRA210_PEQ_STATUS:
case TEGRA210_PEQ_CONFIG:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA:
return true;
default:
return false;
}
}
static bool tegra210_peq_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_PEQ_SOFT_RESET:
case TEGRA210_PEQ_STATUS:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA:
return true;
default:
return false;
}
}
static bool tegra210_peq_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA:
case TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA:
return true;
default:
return false;
}
}
static const struct regmap_config tegra210_peq_regmap_config = {
.name = "peq",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA,
.writeable_reg = tegra210_peq_wr_reg,
.readable_reg = tegra210_peq_rd_reg,
.volatile_reg = tegra210_peq_volatile_reg,
.precious_reg = tegra210_peq_precious_reg,
.reg_defaults = tegra210_peq_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
void tegra210_peq_restore(struct tegra210_ope *ope)
{
unsigned int i;
for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) {
tegra210_ahub_write_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA,
(i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH),
(u32 *)&ope->peq_biquad_gains,
TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH);
tegra210_ahub_write_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA,
(i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH),
(u32 *)&ope->peq_biquad_shifts,
TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH);
}
}
EXPORT_SYMBOL_GPL(tegra210_peq_restore);
void tegra210_peq_save(struct tegra210_ope *ope)
{
unsigned int i;
for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) {
tegra210_ahub_read_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA,
(i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH),
(u32 *)&ope->peq_biquad_gains,
TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH);
tegra210_ahub_read_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA,
(i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH),
(u32 *)&ope->peq_biquad_shifts,
TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH);
}
}
EXPORT_SYMBOL_GPL(tegra210_peq_save);
int tegra210_peq_codec_init(struct snd_soc_component *cmpnt)
{
struct tegra210_ope *ope = snd_soc_component_get_drvdata(cmpnt);
int i = 0;
pm_runtime_get_sync(cmpnt->dev);
regmap_update_bits(ope->peq_regmap, TEGRA210_PEQ_CONFIG,
TEGRA210_PEQ_CONFIG_MODE_MASK,
0 << TEGRA210_PEQ_CONFIG_MODE_SHIFT);
regmap_update_bits(ope->peq_regmap, TEGRA210_PEQ_CONFIG,
TEGRA210_PEQ_CONfIG_BIQUAD_STAGES_MASK,
(biquad_init_stage - 1) <<
TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT);
/* Initialize PEQ AHUB RAM with default params */
for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) {
/* Set default gain params */
tegra210_ahub_write_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA,
(i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH),
(u32 *)&biquad_init_gains,
TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH);
/* Set default shift params */
tegra210_ahub_write_ram(ope->peq_regmap,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL,
TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA,
(i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH),
(u32 *)&biquad_init_shifts,
TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH);
}
pm_runtime_put_sync(cmpnt->dev);
snd_soc_add_component_controls(cmpnt, tegra210_peq_controls,
ARRAY_SIZE(tegra210_peq_controls));
return 0;
}
EXPORT_SYMBOL_GPL(tegra210_peq_codec_init);
int tegra210_peq_regmap_init(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_ope *ope = dev_get_drvdata(dev);
struct device_node *child;
struct resource mem;
void __iomem *regs;
int err;
child = of_get_child_by_name(dev->of_node, "equalizer");
if (!child)
return -ENODEV;
err = of_address_to_resource(child, 0, &mem);
of_node_put(child);
if (err < 0) {
dev_err(dev, "fail to get PEQ resource\n");
return err;
}
mem.flags = IORESOURCE_MEM;
regs = devm_ioremap_resource(dev, &mem);
if (IS_ERR(regs))
return PTR_ERR(regs);
ope->peq_regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_peq_regmap_config);
if (IS_ERR(ope->peq_regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(ope->peq_regmap);
}
regcache_cache_only(ope->peq_regmap, true);
return 0;
}
EXPORT_SYMBOL_GPL(tegra210_peq_regmap_init);
MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
MODULE_DESCRIPTION("Tegra210 PEQ module");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_peq.h - Definitions for Tegra210 PEQ driver
*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_PEQ_H__
#define __TEGRA210_PEQ_H__
/* Register offsets from TEGRA210_PEQ*_BASE */
#define TEGRA210_PEQ_SOFT_RESET 0x0
#define TEGRA210_PEQ_CG 0x4
#define TEGRA210_PEQ_STATUS 0x8
#define TEGRA210_PEQ_CONFIG 0xc
#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_CTRL 0x10
#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_DATA 0x14
#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_CTRL 0x18
#define TEGRA210_PEQ_AHUBRAMCTL_CONFIG_RAM_SHIFT_DATA 0x1c
/* Fields in TEGRA210_PEQ_CONFIG */
#define TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT 2
#define TEGRA210_PEQ_CONfIG_BIQUAD_STAGES_MASK (0xf << TEGRA210_PEQ_CONFIG_BIQUAD_STAGES_SHIFT)
#define TEGRA210_PEQ_CONFIG_BIAS_SHIFT 1
#define TEGRA210_PEQ_CONFIG_BIAS_MASK (0x1 << TEGRA210_PEQ_CONFIG_BIAS_SHIFT)
#define TEGRA210_PEQ_CONFIG_UNBIAS (1 << TEGRA210_PEQ_CONFIG_BIAS_SHIFT)
#define TEGRA210_PEQ_CONFIG_MODE_SHIFT 0
#define TEGRA210_PEQ_CONFIG_MODE_MASK (0x1 << TEGRA210_PEQ_CONFIG_MODE_SHIFT)
#define TEGRA210_PEQ_CONFIG_MODE_ACTIVE (1 << TEGRA210_PEQ_CONFIG_MODE_SHIFT)
/* PEQ register definition ends here */
#define TEGRA210_PEQ_MAX_BIQUAD_STAGES 12
#define TEGRA210_PEQ_MAX_CHANNELS 8
#define TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH \
(2 + TEGRA210_PEQ_MAX_BIQUAD_STAGES * 5)
#define TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH \
(2 + TEGRA210_PEQ_MAX_BIQUAD_STAGES)
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tegra210_sfc.h - Definitions for Tegra210 SFC driver
*
* Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
*
*/
#ifndef __TEGRA210_SFC_H__
#define __TEGRA210_SFC_H__
/*
* SFC_RX registers are with respect to AXBAR.
* The data is coming from AXBAR to SFC for playback.
*/
#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 AXBAR.
* The data is going out of SFC for playback.
*/
#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 13
/* Fields in TEGRA210_SFC_COEF_RAM */
#define TEGRA210_SFC_COEF_RAM_EN BIT(0)
#define TEGRA210_SFC_SOFT_RESET_EN BIT(0)
/* SRC coefficients */
#define TEGRA210_SFC_COEF_RAM_DEPTH 64
enum tegra210_sfc_path {
SFC_RX_PATH,
SFC_TX_PATH,
SFC_PATHS,
};
struct tegra210_sfc {
int srate_in;
int srate_out;
int format_in;
int format_out;
struct regmap *regmap;
struct snd_pcm_hw_params in_hw_params;
struct snd_pcm_hw_params out_hw_params;
int audio_ch_override[SFC_PATHS];
int client_ch_override; /* common for both TX and RX */
int stereo_to_mono[SFC_PATHS];
int mono_to_stereo[SFC_PATHS];
};
#endif

View File

@@ -12,6 +12,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "tegra210_ahub.h"
#include "tegra_asoc_machine.h" #include "tegra_asoc_machine.h"
#include "tegra_codecs.h" #include "tegra_codecs.h"