mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
ASoC: tegra: Per channel vol/mute support
Per channel volume/mute control is possible with MVC and the support is enabled through mixer ctrls for volume settings of each channel and per channel mute mask while retaining previous Mute and Vol controls. Eventually Mute and Vol would be deprecated Bug 200532059 Change-Id: I7c731276cfc6df7d268e398215d18a978213e521 Signed-off-by: Asha Talambedu <atalambedu@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2400678 (cherry picked from commit 74eaf197c10db33631b574c1c3ac8cce82af925a) Reviewed-on: https://git-master.nvidia.com/r/c/linux-5.10/+/2535583 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: Sameer Pujar <spujar@nvidia.com> Reviewed-by: Viswanath L <viswanathl@nvidia.com> Reviewed-by: Sharad Gupta <sharadg@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
committed by
Sameer Pujar
parent
b80b630e1d
commit
a740b0f4eb
@@ -28,7 +28,7 @@ static const struct reg_default tegra210_mvc_reg_defaults[] = {
|
||||
{ TEGRA210_MVC_TX_INT_MASK, 0x00000001},
|
||||
{ TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
|
||||
{ TEGRA210_MVC_CG, 0x1},
|
||||
{ TEGRA210_MVC_CTRL, 0x40000001},
|
||||
{ TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
|
||||
{ TEGRA210_MVC_INIT_VOL, 0x00800000},
|
||||
{ TEGRA210_MVC_TARGET_VOL, 0x00800000},
|
||||
{ TEGRA210_MVC_DURATION, 0x000012c0},
|
||||
@@ -43,6 +43,7 @@ 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);
|
||||
|
||||
@@ -55,9 +56,10 @@ static int tegra210_mvc_runtime_resume(struct device *dev)
|
||||
|
||||
regcache_cache_only(mvc->regmap, false);
|
||||
regcache_sync(mvc->regmap);
|
||||
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
|
||||
TEGRA210_MVC_CURVE_TYPE_MASK,
|
||||
mvc->curve_type << TEGRA210_MVC_CURVE_TYPE_SHIFT);
|
||||
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;
|
||||
}
|
||||
@@ -96,8 +98,31 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
|
||||
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
|
||||
unsigned int reg = mc->reg;
|
||||
|
||||
if (reg == TEGRA210_MVC_TARGET_VOL) {
|
||||
s32 val = mvc->volume;
|
||||
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")) {
|
||||
ucontrol->value.integer.value[0] = mute_mask;
|
||||
} else {
|
||||
if (mute_mask == TEGRA210_MUTE_MASK_EN ||
|
||||
mute_mask == 0) {
|
||||
ucontrol->value.integer.value[0] =
|
||||
mute_mask ? 1 : 0;
|
||||
} else {
|
||||
dev_err(cmpnt->dev, "Use Per Chan Mute Mask!");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
@@ -106,16 +131,26 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
|
||||
val += 12000;
|
||||
}
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
} else {
|
||||
u32 val;
|
||||
|
||||
regmap_read(mvc->regmap, reg, &val);
|
||||
ucontrol->value.integer.value[0] =
|
||||
((val & TEGRA210_MVC_MUTE_MASK) != 0);
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -125,8 +160,7 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
|
||||
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
|
||||
unsigned int reg = mc->reg;
|
||||
unsigned int value;
|
||||
int err;
|
||||
s32 val;
|
||||
int err, i;
|
||||
|
||||
pm_runtime_get_sync(cmpnt->dev);
|
||||
|
||||
@@ -137,41 +171,95 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
if (reg == TEGRA210_MVC_TARGET_VOL) {
|
||||
/* 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) {
|
||||
val = ucontrol->value.integer.value[0];
|
||||
if (val > 10000)
|
||||
val = 10000;
|
||||
mvc->volume = ((val * (1<<8)) / 100) << 16;
|
||||
} else {
|
||||
val = ucontrol->value.integer.value[0] - 12000;
|
||||
mvc->volume = (val * (1<<8)) / 100;
|
||||
}
|
||||
err = regmap_write(mvc->regmap, reg, mvc->volume);
|
||||
} else {
|
||||
if (reg == TEGRA210_MVC_CTRL) {
|
||||
u8 mute_mask;
|
||||
|
||||
if (strstr(kcontrol->id.name, "Per Chan Mute Mask"))
|
||||
mute_mask = ucontrol->value.integer.value[0];
|
||||
else
|
||||
mute_mask =
|
||||
ucontrol->value.integer.value[0] ?
|
||||
TEGRA210_MUTE_MASK_EN : 0;
|
||||
|
||||
err = regmap_update_bits(mvc->regmap, reg,
|
||||
TEGRA210_MVC_MUTE_MASK,
|
||||
(ucontrol->value.integer.value[0] ?
|
||||
TEGRA210_MVC_MUTE_EN : 0));
|
||||
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_write(mvc->regmap,
|
||||
TEGRA210_MVC_REG_OFFSET(
|
||||
TEGRA210_MVC_INIT_VOL, chan),
|
||||
mvc->volume[chan]);
|
||||
err = regmap_write(mvc->regmap, reg, mvc->volume[chan]);
|
||||
} else {
|
||||
for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) {
|
||||
if (i != 0)
|
||||
mvc->volume[i] = mvc->volume[0];
|
||||
regmap_write(mvc->regmap,
|
||||
TEGRA210_MVC_REG_OFFSET(
|
||||
TEGRA210_MVC_INIT_VOL, i),
|
||||
mvc->volume[i]);
|
||||
err |= regmap_write(mvc->regmap,
|
||||
TEGRA210_MVC_REG_OFFSET(reg, i),
|
||||
mvc->volume[i]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err |= regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
|
||||
TEGRA210_MVC_VOLUME_SWITCH_MASK,
|
||||
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
|
||||
|
||||
end:
|
||||
pm_runtime_put(cmpnt->dev);
|
||||
|
||||
if (reg == TEGRA210_MVC_TARGET_VOL)
|
||||
err |= regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
|
||||
TEGRA210_MVC_MUTE_MASK, 0);
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -188,13 +276,21 @@ static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol,
|
||||
{
|
||||
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];
|
||||
/* change volume to default init for new curve type */
|
||||
if (mvc->curve_type == CURVE_POLY)
|
||||
mvc->volume = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
|
||||
else
|
||||
mvc->volume = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
|
||||
|
||||
tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -341,22 +437,6 @@ static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* disable per_channel_cntrl_en */
|
||||
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
|
||||
TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
|
||||
~(TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK));
|
||||
|
||||
/* change curve type */
|
||||
err = regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
|
||||
TEGRA210_MVC_CURVE_TYPE_MASK,
|
||||
mvc->curve_type <<
|
||||
TEGRA210_MVC_CURVE_TYPE_SHIFT);
|
||||
|
||||
/* init the default volume=1 for MVC */
|
||||
regmap_write(mvc->regmap, TEGRA210_MVC_INIT_VOL, mvc->volume);
|
||||
|
||||
regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL, mvc->volume);
|
||||
|
||||
/* program the poly coefficients */
|
||||
for (i = 0; i < 9; i++) {
|
||||
err = tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]);
|
||||
@@ -375,10 +455,6 @@ static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
|
||||
/* program duration_inv */
|
||||
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, mvc->duration_inv);
|
||||
|
||||
/* trigger volume switch */
|
||||
err |= regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
|
||||
TEGRA210_MVC_VOLUME_SWITCH_MASK,
|
||||
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -410,11 +486,29 @@ static const struct soc_enum tegra210_mvc_format_enum =
|
||||
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),
|
||||
/* Will be deprecated in future */
|
||||
SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
|
||||
tegra210_mvc_get_vol, tegra210_mvc_put_vol),
|
||||
/* Will be deprecated in future */
|
||||
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,
|
||||
@@ -519,6 +613,7 @@ static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
|
||||
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;
|
||||
@@ -571,7 +666,7 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev)
|
||||
mvc->poly_coeff[7] = 5527252;
|
||||
mvc->poly_coeff[8] = -785042;
|
||||
mvc->curve_type = CURVE_LINEAR;
|
||||
mvc->volume = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
|
||||
mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
@@ -595,7 +690,7 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,9 @@
|
||||
#define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT)
|
||||
|
||||
#define TEGRA210_MVC_MUTE_SHIFT 8
|
||||
#define TEGRA210_MVC_MUTE_MASK (0xff << TEGRA210_MVC_MUTE_SHIFT)
|
||||
#define TEGRA210_MVC_MUTE_EN (0xff << TEGRA210_MVC_MUTE_SHIFT)
|
||||
#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_PER_CHAN_CTRL_EN_SHIFT 30
|
||||
#define TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK (1 << TEGRA210_MVC_PER_CHAN_CTRL_EN_SHIFT)
|
||||
@@ -75,6 +76,7 @@
|
||||
#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
|
||||
@@ -92,6 +94,10 @@
|
||||
#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,
|
||||
@@ -101,8 +107,9 @@ struct tegra210_mvc {
|
||||
struct regmap *regmap;
|
||||
int poly_coeff[9];
|
||||
int poly_n1, poly_n2, duration, duration_inv;
|
||||
int volume;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user