Files
linux-nv-oot/sound/soc/tegra/tegra210_peq.c
Sameer Pujar c4ab6f5a0e ASoC: tegra: Align with new device model
PEQ and MBDRC are sub blocks of OPE module. So far, these sub blocks
were not represented by separate device nodes in DT. This has changed
now where PEQ and MBDRC are children of OPE block. For now the driver
structure is maintained as it is and explicitly parse PEQ and MBDRC
nodes to create corresponding regmap interfaces. With this there is
flexibility now to create separate device drivers for PEQ and MBDRC
if necessary in future.

Bug 3583581

Change-Id: I16ceb9b1a30b4ddb1a57fdef28db20c554c58b03
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2783022
Reviewed-by: Mohan Kumar D <mkumard@nvidia.com>
Reviewed-by: Sharad Gupta <sharadg@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2022-10-01 10:51:57 -07:00

398 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_peq.c - Tegra210 PEQ driver
//
// Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_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");