mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Revert "Revert "ASoC: tegra: Remove legacy AHUB OOT drivers""
This reverts commit 00c10d965b.
This is done to restore original commit of removing legacy AHUB OOT
drivers as the GVS intermittency issue is now root caused.
Bug 4508166
Change-Id: I0f0f9d34b0c408aa4fec20bc6df25ea23eac1189
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3083108
Reviewed-by: Mohan kumar <mkumard@nvidia.com>
Reviewed-by: Sharad Gupta <sharadg@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
b0b6f09a6d
commit
72ed0ddf9d
@@ -1,42 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2023-2024 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,8 +1,7 @@
|
||||
// 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>
|
||||
|
||||
@@ -27,7 +26,10 @@
|
||||
#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;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,176 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,711 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,73 +0,0 @@
|
||||
/* 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,168 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,776 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,96 +0,0 @@
|
||||
/* 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,8 +1,7 @@
|
||||
// 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>
|
||||
|
||||
@@ -23,7 +22,6 @@
|
||||
#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},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,145 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,870 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,116 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,717 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,85 +0,0 @@
|
||||
/* 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,133 +0,0 @@
|
||||
/* 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,8 +1,7 @@
|
||||
// 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>
|
||||
@@ -20,7 +19,6 @@
|
||||
|
||||
#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[] = {
|
||||
|
||||
@@ -1,912 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,238 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,715 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,112 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,779 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,124 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,387 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,103 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,397 +0,0 @@
|
||||
// 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");
|
||||
@@ -1,43 +0,0 @@
|
||||
/* 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
/* 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,7 +12,6 @@
|
||||
#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