blob: 86a696b1df70bf87b122da890f4423400eff74fc [file] [log] [blame]
/*
* sound/soc/amlogic/auge/effects_v2.c
*
* Copyright (C) 2018 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "effects_v2.h"
#include "effects_hw_v2.h"
#include "effects_hw_v2_coeff.h"
#include "ddr_mngr.h"
#include "regs.h"
#include "iomap.h"
#define DRV_NAME "Effects"
/*
* AED Diagram
* DC -- ND -- MIX -- EQ -- Mutiband DRC -- LR Vol
* -- Fullband DRC -- Master Volume
*/
enum {
AED_DC,
AED_ND,
AED_EQ,
AED_MDRC,
AED_FDRC
};
struct effect_chipinfo {
/* v1 is for G12X(g12a, g12b)
* v2 is for tl1
*/
bool v2;
};
struct audioeffect {
struct device *dev;
/* gate */
struct clk *gate;
/* source mpll */
struct clk *srcpll;
/* eqdrc clk */
struct clk *clk;
struct effect_chipinfo *chipinfo;
bool dc_en;
bool nd_en;
bool eq_en;
bool multiband_drc_en;
bool fullband_drc_en;
int lane_mask;
int ch_mask;
/*which module should be effected */
int effect_module;
};
struct audioeffect *s_effect;
static struct audioeffect *get_audioeffects(void)
{
if (!s_effect) {
pr_info("Not init audio effects\n");
return NULL;
}
return s_effect;
}
bool check_aed_v2(void)
{
struct audioeffect *p_effect = get_audioeffects();
if (!p_effect)
return false;
if (p_effect->chipinfo && p_effect->chipinfo->v2)
return true;
return false;
}
static int eqdrc_clk_set(struct audioeffect *p_effect)
{
int ret = 0;
ret = clk_prepare_enable(p_effect->clk);
if (ret) {
pr_err("Can't enable eqdrc clock: %d\n",
ret);
return -EINVAL;
}
ret = clk_prepare_enable(p_effect->srcpll);
if (ret) {
pr_err("Can't enable eqdrc src pll clock: %d\n",
ret);
return -EINVAL;
}
/* defaule clk */
clk_set_rate(p_effect->clk, 200000000);
pr_info("%s, src pll:%lu, clk:%lu\n",
__func__,
clk_get_rate(p_effect->srcpll),
clk_get_rate(p_effect->clk));
return 0;
}
static int mixer_aed_read(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int invert = mc->invert;
unsigned int value =
(((unsigned int)eqdrc_read(reg)) >> shift) & max;
if (invert)
value = (~value) & max;
ucontrol->value.integer.value[0] = value;
return 0;
}
static int mixer_aed_write(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int invert = mc->invert;
unsigned int value = ucontrol->value.integer.value[0];
unsigned int new_val = (unsigned int)eqdrc_read(reg);
if (invert)
value = (~value) & max;
max = ~(max << shift);
new_val &= max;
new_val |= (value << shift);
eqdrc_write(reg, new_val);
return 0;
}
static int mixer_get_EQ_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int *value = (unsigned int *)ucontrol->value.bytes.data;
unsigned int *p = &EQ_COEFF[0];
int i;
aed_get_ram_coeff(EQ_FILTER_RAM_ADD, EQ_FILTER_SIZE_CH, p);
for (i = 0; i < EQ_FILTER_SIZE_CH; i++)
*value++ = cpu_to_be32(*p++);
return 0;
}
static int str2int(char *str, unsigned int *data, int size)
{
int num = 0;
unsigned int temp = 0;
char *ptr = str;
unsigned int *val = data;
while (size-- != 0) {
if ((*ptr >= '0') && (*ptr <= '9')) {
temp = temp * 16 + (*ptr - '0');
} else if ((*ptr >= 'a') && (*ptr <= 'f')) {
temp = temp * 16 + (*ptr - 'a' + 10);
} else if (*ptr == ' ') {
*(val+num) = temp;
temp = 0;
num++;
}
ptr++;
}
return num;
}
static int mixer_set_EQ_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int tmp_data[FILTER_PARAM_SIZE + 1];
unsigned int *p_data = &tmp_data[0];
char tmp_string[FILTER_PARAM_BYTE];
char *p_string = &tmp_string[0];
unsigned int *p = &EQ_COEFF[0];
int num, i, band_id;
char *val = (char *)ucontrol->value.bytes.data;
if (!val)
return -ENOMEM;
memcpy(p_string, val, FILTER_PARAM_BYTE);
num = str2int(p_string, p_data, FILTER_PARAM_BYTE);
band_id = tmp_data[0];
if (num != (FILTER_PARAM_SIZE + 1) || band_id >= EQ_BAND) {
pr_info("Error: parma_num = %d, band_id = %d\n",
num, tmp_data[0]);
return 0;
}
p_data = &tmp_data[1];
p = &EQ_COEFF[band_id*FILTER_PARAM_SIZE];
for (i = 0; i < FILTER_PARAM_SIZE; i++, p++, p_data++) {
*p = *p_data;
*(p + EQ_FILTER_SIZE_CH) = *p_data;
}
p = &EQ_COEFF[band_id*FILTER_PARAM_SIZE];
aed_set_ram_coeff((EQ_FILTER_RAM_ADD +
band_id*FILTER_PARAM_SIZE),
FILTER_PARAM_SIZE, p);
p = &EQ_COEFF[band_id*FILTER_PARAM_SIZE + EQ_FILTER_SIZE_CH];
aed_set_ram_coeff((EQ_FILTER_RAM_ADD +
EQ_FILTER_SIZE_CH + band_id*FILTER_PARAM_SIZE),
FILTER_PARAM_SIZE, p);
return 0;
}
static int mixer_get_crossover_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int *value = (unsigned int *)ucontrol->value.bytes.data;
unsigned int *p = &CROSSOVER_COEFF[0];
int i;
aed_get_ram_coeff(CROSSOVER_FILTER_RAM_ADD, CROSSOVER_FILTER_SIZE, p);
for (i = 0; i < CROSSOVER_FILTER_SIZE; i++)
*value++ = cpu_to_be32(*p++);
return 0;
}
static int mixer_set_crossover_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int tmp_data[FILTER_PARAM_SIZE + 1];
unsigned int *p_data = &tmp_data[0];
char tmp_string[FILTER_PARAM_BYTE];
char *p_string = &tmp_string[0];
unsigned int *p = &CROSSOVER_COEFF[0];
int num, i, band_id;
char *val = (char *)ucontrol->value.bytes.data;
if (!val)
return -ENOMEM;
memcpy(p_string, val, FILTER_PARAM_BYTE);
num = str2int(p_string, p_data, FILTER_PARAM_BYTE);
band_id = tmp_data[0];
if (num != (FILTER_PARAM_SIZE + 1) ||
band_id >= CROSSOVER_FILTER_BAND) {
pr_info("Error: parma_num = %d, band_id = %d\n",
num, tmp_data[0]);
return 0;
}
p_data = &tmp_data[1];
p = &CROSSOVER_COEFF[band_id*FILTER_PARAM_SIZE];
for (i = 0; i < FILTER_PARAM_SIZE; i++)
*p++ = *p_data++;
p = &CROSSOVER_COEFF[band_id*FILTER_PARAM_SIZE];
aed_set_ram_coeff((CROSSOVER_FILTER_RAM_ADD +
band_id*FILTER_PARAM_SIZE),
FILTER_PARAM_SIZE, p);
return 0;
}
static int mixer_get_multiband_DRC_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int *value = (unsigned int *)ucontrol->value.bytes.data;
unsigned int *p = &multiband_drc_coeff[0];
int i;
for (i = 0; i < 3; i++) {
aed_get_multiband_drc_coeff(i,
p + i * AED_SINGLE_BAND_DRC_SIZE);
}
for (i = 0; i < AED_MULTIBAND_DRC_SIZE; i++)
*value++ = cpu_to_be32(*p++);
return 0;
}
static int mixer_set_multiband_DRC_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int tmp_data[AED_SINGLE_BAND_DRC_SIZE + 1];
unsigned int *p_data = &tmp_data[0];
char tmp_string[MULTIBAND_DRC_PARAM_BYTE];
char *p_string = &tmp_string[0];
unsigned int *p = &multiband_drc_coeff[0];
int num, i, band_id;
char *val = (char *)ucontrol->value.bytes.data;
if (!val)
return -ENOMEM;
memcpy(p_string, val, MULTIBAND_DRC_PARAM_BYTE);
num = str2int(p_string, p_data, MULTIBAND_DRC_PARAM_BYTE);
band_id = tmp_data[0];
if (num != (AED_SINGLE_BAND_DRC_SIZE + 1) ||
band_id >= AED_MULTIBAND_DRC_BANDS) {
pr_info("Error: parma_num = %d, band_id = %d\n",
num, tmp_data[0]);
return 0;
}
/*Don't update offset and gain*/
p_data = &tmp_data[1];
p = &multiband_drc_coeff[band_id*AED_SINGLE_BAND_DRC_SIZE];
for (i = 0; i < (AED_SINGLE_BAND_DRC_SIZE - 2) ; i++)
*p++ = *p_data++;
p = &multiband_drc_coeff[0];
aed_set_multiband_drc_coeff(band_id, p);
return 0;
}
static int mixer_get_fullband_DRC_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int *value = (unsigned int *)ucontrol->value.bytes.data;
unsigned int *p = &fullband_drc_coeff[0];
int i;
aed_get_fullband_drc_coeff(AED_FULLBAND_DRC_SIZE, p);
for (i = 0; i < AED_FULLBAND_DRC_SIZE; i++)
*value++ = cpu_to_be32(*p++);
return 0;
}
static int mixer_set_fullband_DRC_params(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int tmp_data[AED_FULLBAND_DRC_OFFSET + 1];
unsigned int *p_data = &tmp_data[0];
char tmp_string[AED_FULLBAND_DRC_BYTES];
char *p_string = &tmp_string[0];
unsigned int *p = &fullband_drc_coeff[0];
int num, i, band_id;
char *val = (char *)ucontrol->value.bytes.data;
if (!val)
return -ENOMEM;
memcpy(p_string, val, AED_FULLBAND_DRC_BYTES);
num = str2int(p_string, p_data, AED_FULLBAND_DRC_BYTES);
band_id = tmp_data[0];
if (num != (AED_FULLBAND_DRC_OFFSET + 1) ||
band_id >= AED_FULLBAND_DRC_GROUP_SIZE) {
pr_info("Error: parma_num = %d, band_id = %d\n",
num, tmp_data[0]);
return 0;
}
p_data = &tmp_data[1];
p = &fullband_drc_coeff[band_id*AED_FULLBAND_DRC_OFFSET];
for (i = 0; i < AED_FULLBAND_DRC_OFFSET; i++)
*p++ = *p_data++;
p = &fullband_drc_coeff[band_id*AED_FULLBAND_DRC_OFFSET];
aed_set_fullband_drc_coeff(band_id, p);
return 0;
}
/* aed module
* check to sync with enum frddr_dest in ddr_mngr.h
*/
static const char *const aed_module_texts[] = {
"TDMOUT_A",
"TDMOUT_B",
"TDMOUT_C",
"SPDIFOUT_A",
"SPDIFOUT_B",
};
static const struct soc_enum aed_module_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(aed_module_texts),
aed_module_texts);
static int aed_module_get_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioeffect *p_effect = snd_kcontrol_chip(kcontrol);
if (!p_effect)
return -EINVAL;
ucontrol->value.enumerated.item[0] = p_effect->effect_module;
return 0;
}
static int aed_module_set_enum(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audioeffect *p_effect = snd_kcontrol_chip(kcontrol);
if (!p_effect)
return -EINVAL;
p_effect->effect_module = ucontrol->value.enumerated.item[0];
/* update info to ddr and modules */
aml_set_aed(1, p_effect->effect_module);
return 0;
}
static void aed_set_filter_data(void)
{
int *p;
/* set default filter param*/
p = &DC_CUT_COEFF[0];
aed_set_ram_coeff(DC_CUT_FILTER_RAM_ADD, DC_CUT_FILTER_SIZE, p);
p = &EQ_COEFF[0];
aed_set_ram_coeff(EQ_FILTER_RAM_ADD, EQ_FILTER_SIZE, p);
p = &CROSSOVER_COEFF[0];
aed_set_ram_coeff(CROSSOVER_FILTER_RAM_ADD, CROSSOVER_FILTER_SIZE, p);
}
static void aed_set_drc_data(void)
{
unsigned int *p = &multiband_drc_coeff[0];
int i;
/*set MDRC default value*/
for (i = 0; i < AED_MULTIBAND_DRC_BANDS; i++)
aed_set_multiband_drc_coeff(i, p);
/*set FDRC default value*/
p = &fullband_drc_coeff[0];
for (i = 0; i < AED_FULLBAND_DRC_GROUP_SIZE; i++) {
aed_set_fullband_drc_coeff(i,
p + i * AED_FULLBAND_DRC_OFFSET);
}
}
static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12276, 12, 1);
static const DECLARE_TLV_DB_SCALE(lr_vol_tlv, -12750, 50, 1);
static const struct snd_kcontrol_new snd_effect_controls[] = {
SOC_SINGLE_EXT("AED DC cut enable",
AED_DC_EN, 0, 0x1, 0,
mixer_aed_read, mixer_aed_write),
SOC_SINGLE_EXT("AED Noise Detect enable",
AED_ND_CNTL, 0, 0x1, 0,
mixer_aed_read, mixer_aed_write),
SOC_SINGLE_EXT("AED EQ enable",
AED_EQ_EN, 0, 0x1, 0,
mixer_aed_read, mixer_aed_write),
SND_SOC_BYTES_EXT("AED EQ Parameters",
(EQ_FILTER_SIZE_CH * 4),
mixer_get_EQ_params,
mixer_set_EQ_params),
SOC_SINGLE_EXT("AED Multi-band DRC enable",
AED_MDRC_CNTL, 8, 0x1, 0,
mixer_aed_read, mixer_aed_write),
SND_SOC_BYTES_EXT("AED Crossover Filter Parameters",
(CROSSOVER_FILTER_SIZE * 4),
mixer_get_crossover_params,
mixer_set_crossover_params),
SND_SOC_BYTES_EXT("AED Multi-band DRC Parameters",
(AED_MULTIBAND_DRC_SIZE * 4),
mixer_get_multiband_DRC_params,
mixer_set_multiband_DRC_params),
SOC_SINGLE_EXT("AED Full-band DRC enable",
AED_DRC_CNTL, 0, 0x1, 0,
mixer_aed_read, mixer_aed_write),
SND_SOC_BYTES_EXT("AED Full-band DRC Parameters",
AED_FULLBAND_DRC_BYTES,
mixer_get_fullband_DRC_params,
mixer_set_fullband_DRC_params),
SOC_ENUM_EXT("AED module",
aed_module_enum,
aed_module_get_enum,
aed_module_set_enum),
SOC_SINGLE_EXT_TLV("AED Lch volume",
AED_EQ_VOLUME, 0, 0xFF, 1,
mixer_aed_read, mixer_aed_write,
lr_vol_tlv),
SOC_SINGLE_EXT_TLV("AED Rch volume",
AED_EQ_VOLUME, 8, 0xFF, 1,
mixer_aed_read, mixer_aed_write,
lr_vol_tlv),
SOC_SINGLE_EXT_TLV("AED master volume",
AED_EQ_VOLUME, 16, 0x3FF, 1,
mixer_aed_read, mixer_aed_write,
master_vol_tlv),
};
int card_add_effect_v2_kcontrols(struct snd_soc_card *card)
{
unsigned int idx;
int err;
if (!s_effect) {
pr_info("effect_v2 is not init\n");
return 0;
}
for (idx = 0; idx < ARRAY_SIZE(snd_effect_controls); idx++) {
err = snd_ctl_add(card->snd_card,
snd_ctl_new1(&snd_effect_controls[idx],
s_effect));
if (err < 0)
return err;
}
return 0;
}
static struct effect_chipinfo tl1_effect_chipinfo = {
.v2 = true,
};
static const struct of_device_id effect_device_id[] = {
{
.compatible = "amlogic, snd-effect-v1"
},
{
.compatible = "amlogic, snd-effect-v2",
.data = &tl1_effect_chipinfo,
},
{
.compatible = "amlogic, tl1-effect",
.data = &tl1_effect_chipinfo,
},
{}
};
MODULE_DEVICE_TABLE(of, effect_device_id);
static int effect_platform_probe(struct platform_device *pdev)
{
struct audioeffect *p_effect;
struct device *dev = &pdev->dev;
struct effect_chipinfo *p_chipinfo;
bool eq_enable = false;
bool multiband_drc_enable = false;
bool fullband_drc_enable = false;
int lane_mask = -1, channel_mask = -1, eqdrc_module = -1;
int ret;
pr_info("%s, line:%d\n", __func__, __LINE__);
p_effect = devm_kzalloc(&pdev->dev,
sizeof(struct audioeffect),
GFP_KERNEL);
if (!p_effect) {
dev_err(&pdev->dev, "Can't allocate pcm_p\n");
return -ENOMEM;
}
/* match data */
p_chipinfo = (struct effect_chipinfo *)
of_device_get_match_data(dev);
if (!p_chipinfo)
dev_warn_once(dev, "check whether to update effect chipinfo\n");
p_effect->chipinfo = p_chipinfo;
p_effect->gate = devm_clk_get(&pdev->dev, "gate");
if (IS_ERR(p_effect->gate)) {
dev_err(&pdev->dev,
"Can't retrieve eqdrc gate clock\n");
ret = PTR_ERR(p_effect->gate);
return ret;
}
p_effect->srcpll = devm_clk_get(&pdev->dev, "srcpll");
if (IS_ERR(p_effect->srcpll)) {
dev_err(&pdev->dev,
"Can't retrieve source mpll clock\n");
ret = PTR_ERR(p_effect->srcpll);
return ret;
}
p_effect->clk = devm_clk_get(&pdev->dev, "eqdrc");
if (IS_ERR(p_effect->clk)) {
dev_err(&pdev->dev,
"Can't retrieve eqdrc clock\n");
ret = PTR_ERR(p_effect->clk);
return ret;
}
ret = clk_set_parent(p_effect->clk, p_effect->srcpll);
if (ret) {
dev_err(&pdev->dev,
"Can't set eqdrc clock parent clock\n");
ret = PTR_ERR(p_effect->clk);
return ret;
}
ret = eqdrc_clk_set(p_effect);
if (ret < 0) {
dev_err(&pdev->dev, "set eq drc module clk fail!\n");
return -EINVAL;
}
ret = of_property_read_u32(pdev->dev.of_node,
"eqdrc_module",
&eqdrc_module);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve eqdrc_module\n");
return -EINVAL;
}
ret = of_property_read_u32(pdev->dev.of_node,
"lane_mask",
&lane_mask);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve lane_mask\n");
return -EINVAL;
}
ret = of_property_read_u32(pdev->dev.of_node,
"channel_mask",
&channel_mask);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve channel_mask\n");
return -EINVAL;
}
pr_info("%s \t module:%d, lane_mask:%d, ch_mask:%d\n",
__func__,
eqdrc_module,
lane_mask,
channel_mask
);
/* config from dts */
p_effect->eq_en = eq_enable;
p_effect->multiband_drc_en = multiband_drc_enable;
p_effect->fullband_drc_en = fullband_drc_enable;
p_effect->lane_mask = lane_mask;
p_effect->ch_mask = channel_mask;
p_effect->effect_module = eqdrc_module;
/*set eq/drc module lane & channels*/
aed_set_lane_and_channels(lane_mask, channel_mask);
/*set master & channel volume gain to 0dB*/
aed_set_volume(0xc0, 0x30, 0x30);
/*set default mixer gain*/
aed_set_mixer_params();
/*all 20 bands for EQ1*/
aed_eq_taps(EQ_BAND);
/*set default filter param*/
aed_set_filter_data();
/*set multi-band drc param*/
aed_set_multiband_drc_param();
/*set multi/full-band drc data*/
aed_set_drc_data();
/*set full-band drc param, enable 2 band*/
aed_set_fullband_drc_param(2);
/*set EQ/DRC module enable*/
aml_set_aed(1, p_effect->effect_module);
p_effect->dev = dev;
s_effect = p_effect;
dev_set_drvdata(&pdev->dev, p_effect);
return 0;
}
static struct platform_driver effect_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(effect_device_id),
},
.probe = effect_platform_probe,
};
module_platform_driver(effect_platform_driver);
MODULE_AUTHOR("AMLogic, Inc.");
MODULE_DESCRIPTION("Amlogic Audio Effects driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);