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:
Asha Talambedu
2021-04-20 14:20:21 +05:30
committed by Sameer Pujar
parent b80b630e1d
commit a740b0f4eb
2 changed files with 170 additions and 68 deletions

View File

@@ -28,7 +28,7 @@ static const struct reg_default tegra210_mvc_reg_defaults[] = {
{ TEGRA210_MVC_TX_INT_MASK, 0x00000001}, { TEGRA210_MVC_TX_INT_MASK, 0x00000001},
{ TEGRA210_MVC_TX_CIF_CTRL, 0x00007700}, { TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
{ TEGRA210_MVC_CG, 0x1}, { TEGRA210_MVC_CG, 0x1},
{ TEGRA210_MVC_CTRL, 0x40000001}, { TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
{ TEGRA210_MVC_INIT_VOL, 0x00800000}, { TEGRA210_MVC_INIT_VOL, 0x00800000},
{ TEGRA210_MVC_TARGET_VOL, 0x00800000}, { TEGRA210_MVC_TARGET_VOL, 0x00800000},
{ TEGRA210_MVC_DURATION, 0x000012c0}, { 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); 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_cache_only(mvc->regmap, true);
regcache_mark_dirty(mvc->regmap); 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_cache_only(mvc->regmap, false);
regcache_sync(mvc->regmap); regcache_sync(mvc->regmap);
regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value);
TEGRA210_MVC_CURVE_TYPE_MASK, regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
mvc->curve_type << TEGRA210_MVC_CURVE_TYPE_SHIFT); TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
return 0; 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); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
if (reg == TEGRA210_MVC_TARGET_VOL) { if (reg == TEGRA210_MVC_CTRL) {
s32 val = mvc->volume; 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) if (mvc->curve_type == CURVE_POLY)
val = ((val >> 16) * 100) >> 8; val = ((val >> 16) * 100) >> 8;
@@ -106,16 +131,26 @@ static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
val += 12000; val += 12000;
} }
ucontrol->value.integer.value[0] = val; 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; 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, static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) 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); struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
unsigned int value; unsigned int value;
int err; int err, i;
s32 val;
pm_runtime_get_sync(cmpnt->dev); pm_runtime_get_sync(cmpnt->dev);
@@ -137,41 +171,95 @@ static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
if (err < 0) if (err < 0)
goto end; goto end;
if (reg == TEGRA210_MVC_TARGET_VOL) { if (reg == TEGRA210_MVC_CTRL) {
/* Volume control read from mixer ctl is with */ u8 mute_mask;
/* 100x scaling; for CURVE_POLY the reg range */
/* is 0-100 (linear, Q24) and for CURVE_LINEAR */ if (strstr(kcontrol->id.name, "Per Chan Mute Mask"))
/* it is -120dB to +40dB (Q8) */ mute_mask = ucontrol->value.integer.value[0];
if (mvc->curve_type == CURVE_POLY) { else
val = ucontrol->value.integer.value[0]; mute_mask =
if (val > 10000) ucontrol->value.integer.value[0] ?
val = 10000; TEGRA210_MUTE_MASK_EN : 0;
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 {
err = regmap_update_bits(mvc->regmap, reg, err = regmap_update_bits(mvc->regmap, reg,
TEGRA210_MVC_MUTE_MASK, TEGRA210_MVC_MUTE_MASK,
(ucontrol->value.integer.value[0] ? mute_mask << TEGRA210_MVC_MUTE_SHIFT);
TEGRA210_MVC_MUTE_EN : 0)); } 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, err |= regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
TEGRA210_MVC_VOLUME_SWITCH_MASK, TEGRA210_MVC_VOLUME_SWITCH_MASK,
TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
end: end:
pm_runtime_put(cmpnt->dev); 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; 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, static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) 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 snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 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]; mvc->curve_type = ucontrol->value.integer.value[0];
/* change volume to default init for new curve type */
if (mvc->curve_type == CURVE_POLY) tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
mvc->volume = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
else
mvc->volume = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
return 0; return 0;
} }
@@ -341,22 +437,6 @@ static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
return err; 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 */ /* program the poly coefficients */
for (i = 0; i < 9; i++) { for (i = 0; i < 9; i++) {
err = tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[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 */ /* program duration_inv */
regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, mvc->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; return err;
} }
@@ -410,11 +486,29 @@ static const struct soc_enum tegra210_mvc_format_enum =
ARRAY_SIZE(tegra210_mvc_format_text), ARRAY_SIZE(tegra210_mvc_format_text),
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[] = { 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, SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
tegra210_mvc_get_vol, tegra210_mvc_put_vol), tegra210_mvc_get_vol, tegra210_mvc_put_vol),
/* Will be deprecated in future */
SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0, SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
tegra210_mvc_get_vol, tegra210_mvc_put_vol), 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, SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
SOC_SINGLE_EXT("Bits", 0, 0, 32, 0, 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_CTRL:
case TEGRA210_MVC_CFG_RAM_DATA: case TEGRA210_MVC_CFG_RAM_DATA:
case TEGRA210_MVC_PEAK_VALUE: case TEGRA210_MVC_PEAK_VALUE:
case TEGRA210_MVC_CTRL:
return true; return true;
default: default:
return false; return false;
@@ -571,7 +666,7 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev)
mvc->poly_coeff[7] = 5527252; mvc->poly_coeff[7] = 5527252;
mvc->poly_coeff[8] = -785042; mvc->poly_coeff[8] = -785042;
mvc->curve_type = CURVE_LINEAR; 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); regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) if (IS_ERR(regs))
@@ -595,7 +690,7 @@ static int tegra210_mvc_platform_probe(struct platform_device *pdev)
} }
pm_runtime_enable(dev); pm_runtime_enable(dev);
tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
return 0; return 0;
} }

View File

@@ -62,8 +62,9 @@
#define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT) #define TEGRA210_MVC_EN (1 << TEGRA210_MVC_EN_SHIFT)
#define TEGRA210_MVC_MUTE_SHIFT 8 #define TEGRA210_MVC_MUTE_SHIFT 8
#define TEGRA210_MVC_MUTE_MASK (0xff << TEGRA210_MVC_MUTE_SHIFT) #define TEGRA210_MUTE_MASK_EN 0xff
#define TEGRA210_MVC_MUTE_EN (0xff << TEGRA210_MVC_MUTE_SHIFT) #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_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_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_SHIFT 2
#define TEGRA210_MVC_VOLUME_SWITCH_MASK (1 << TEGRA210_MVC_VOLUME_SWITCH_SHIFT) #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_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_POLY 0x01000000
#define TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR 0x00000000 #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_SHIFT 0
#define TEGRA210_MVC_CFG_RAM_CTRL_ADDR_MASK (0x1ff << TEGRA210_MVC_CFG_RAM_CTRL_ADDR_SHIFT) #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 { enum {
CURVE_POLY, CURVE_POLY,
CURVE_LINEAR, CURVE_LINEAR,
@@ -101,8 +107,9 @@ struct tegra210_mvc {
struct regmap *regmap; struct regmap *regmap;
int poly_coeff[9]; int poly_coeff[9];
int poly_n1, poly_n2, duration, duration_inv; int poly_n1, poly_n2, duration, duration_inv;
int volume; int volume[TEGRA210_MVC_MAX_CHAN_COUNT];
unsigned int curve_type; unsigned int curve_type;
unsigned int ctrl_value;
unsigned int cif_channels; unsigned int cif_channels;
unsigned int audio_bits; unsigned int audio_bits;
unsigned int format_in; unsigned int format_in;