diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index 01de0a97..8e061226 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -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; } diff --git a/sound/soc/tegra/tegra210_mvc.h b/sound/soc/tegra/tegra210_mvc.h index 5222515b..9231243b 100644 --- a/sound/soc/tegra/tegra210_mvc.h +++ b/sound/soc/tegra/tegra210_mvc.h @@ -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;