mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +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));
|
||||
TEGRA210_MVC_MUTE_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_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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user