mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
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:
committed by
Shubham Chandra
parent
ed7a856cf7
commit
00c10d965b
@@ -1,17 +1,42 @@
|
||||
# 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 \
|
||||
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-mvc-oot-objs := tegra210_mvc.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-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-controls-objs := tegra_mixer_control.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-mvc-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-asrc-oot.o
|
||||
obj-m += snd-soc-tegra-machine-driver-oot.o
|
||||
obj-m += snd-soc-tegra-controls.o
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// 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
|
||||
//
|
||||
// Copyright (c) 2015-2023, NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <nvidia/conftest.h>
|
||||
|
||||
@@ -26,10 +27,7 @@
|
||||
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
|
||||
|
||||
#include "tegra186_arad.h"
|
||||
|
||||
#ifdef CONFIG_SND_SOC_TEGRA186_ASRC_WAR
|
||||
#include "tegra186_asrc.h"
|
||||
#endif
|
||||
|
||||
static struct device *arad_dev;
|
||||
|
||||
|
||||
1179
sound/soc/tegra/tegra186_asrc.c
Normal file
1179
sound/soc/tegra/tegra186_asrc.c
Normal file
File diff suppressed because it is too large
Load Diff
176
sound/soc/tegra/tegra186_asrc.h
Normal file
176
sound/soc/tegra/tegra186_asrc.h
Normal 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
|
||||
711
sound/soc/tegra/tegra186_dspk.c
Normal file
711
sound/soc/tegra/tegra186_dspk.c
Normal 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");
|
||||
73
sound/soc/tegra/tegra186_dspk.h
Normal file
73
sound/soc/tegra/tegra186_dspk.h
Normal 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
|
||||
1410
sound/soc/tegra/tegra210_admaif.c
Normal file
1410
sound/soc/tegra/tegra210_admaif.c
Normal file
File diff suppressed because it is too large
Load Diff
168
sound/soc/tegra/tegra210_admaif.h
Normal file
168
sound/soc/tegra/tegra210_admaif.h
Normal 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
|
||||
776
sound/soc/tegra/tegra210_adx.c
Normal file
776
sound/soc/tegra/tegra210_adx.c
Normal 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 |= 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 |= 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");
|
||||
96
sound/soc/tegra/tegra210_adx.h
Normal file
96
sound/soc/tegra/tegra210_adx.h
Normal 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
|
||||
@@ -1,7 +1,8 @@
|
||||
// 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
|
||||
//
|
||||
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <nvidia/conftest.h>
|
||||
|
||||
@@ -22,6 +23,7 @@
|
||||
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
|
||||
|
||||
#include "tegra210_afc.h"
|
||||
#include "tegra210_ahub.h"
|
||||
|
||||
static const struct reg_default tegra210_afc_reg_defaults[] = {
|
||||
{ TEGRA210_AFC_AXBAR_RX_CIF_CTRL, 0x00007700},
|
||||
|
||||
1696
sound/soc/tegra/tegra210_ahub.c
Normal file
1696
sound/soc/tegra/tegra210_ahub.c
Normal file
File diff suppressed because it is too large
Load Diff
145
sound/soc/tegra/tegra210_ahub.h
Normal file
145
sound/soc/tegra/tegra210_ahub.h
Normal 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
|
||||
870
sound/soc/tegra/tegra210_amx.c
Normal file
870
sound/soc/tegra/tegra210_amx.c
Normal 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 |= 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 |= 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");
|
||||
116
sound/soc/tegra/tegra210_amx.h
Normal file
116
sound/soc/tegra/tegra210_amx.h
Normal 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
|
||||
717
sound/soc/tegra/tegra210_dmic.c
Normal file
717
sound/soc/tegra/tegra210_dmic.c
Normal 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");
|
||||
85
sound/soc/tegra/tegra210_dmic.h
Normal file
85
sound/soc/tegra/tegra210_dmic.h
Normal 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
|
||||
1295
sound/soc/tegra/tegra210_i2s.c
Normal file
1295
sound/soc/tegra/tegra210_i2s.c
Normal file
File diff suppressed because it is too large
Load Diff
133
sound/soc/tegra/tegra210_i2s.h
Normal file
133
sound/soc/tegra/tegra210_i2s.h
Normal 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
|
||||
@@ -1,7 +1,8 @@
|
||||
// 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
|
||||
//
|
||||
// Copyright (c) 2014-2021 NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
@@ -19,6 +20,7 @@
|
||||
|
||||
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
|
||||
|
||||
#include "tegra210_ahub.h"
|
||||
#include "tegra210_iqc.h"
|
||||
|
||||
static const struct reg_default tegra210_iqc_reg_defaults[] = {
|
||||
|
||||
912
sound/soc/tegra/tegra210_mbdrc.c
Normal file
912
sound/soc/tegra/tegra210_mbdrc.c
Normal 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 *)¶ms->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 *)¶ms->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");
|
||||
238
sound/soc/tegra/tegra210_mbdrc.h
Normal file
238
sound/soc/tegra/tegra210_mbdrc.h
Normal 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
|
||||
715
sound/soc/tegra/tegra210_mixer.c
Normal file
715
sound/soc/tegra/tegra210_mixer.c
Normal 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");
|
||||
112
sound/soc/tegra/tegra210_mixer.h
Normal file
112
sound/soc/tegra/tegra210_mixer.h
Normal 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
|
||||
779
sound/soc/tegra/tegra210_mvc.c
Normal file
779
sound/soc/tegra/tegra210_mvc.c
Normal 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");
|
||||
124
sound/soc/tegra/tegra210_mvc.h
Normal file
124
sound/soc/tegra/tegra210_mvc.h
Normal 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
|
||||
387
sound/soc/tegra/tegra210_ope.c
Normal file
387
sound/soc/tegra/tegra210_ope.c
Normal 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");
|
||||
103
sound/soc/tegra/tegra210_ope.h
Normal file
103
sound/soc/tegra/tegra210_ope.h
Normal 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
|
||||
397
sound/soc/tegra/tegra210_peq.c
Normal file
397
sound/soc/tegra/tegra210_peq.c
Normal 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");
|
||||
43
sound/soc/tegra/tegra210_peq.h
Normal file
43
sound/soc/tegra/tegra210_peq.h
Normal 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
|
||||
3520
sound/soc/tegra/tegra210_sfc.c
Normal file
3520
sound/soc/tegra/tegra210_sfc.c
Normal file
File diff suppressed because it is too large
Load Diff
80
sound/soc/tegra/tegra210_sfc.h
Normal file
80
sound/soc/tegra/tegra210_sfc.h
Normal 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
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "tegra210_ahub.h"
|
||||
#include "tegra_asoc_machine.h"
|
||||
#include "tegra_codecs.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user