[PATCH] ASoC: tas2562: Add support for digital volume control
Add support for digital volume control. There is no dedicated register for volume control but instead there are 4. The values of the registers are determined with exponential floating point math. So a table was created with register values for 2dB step increments from -110dB to 0dB.
Signed-off-by: Dan Murphy dmurphy@ti.com --- sound/soc/codecs/tas2562.c | 94 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2562.h | 6 ++- 2 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index b2682c2360b6..e577aa6f304a 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -26,6 +26,24 @@ #define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FORMAT_S32_LE)
+/* DVC equation involves floating point math + * round(10^(volume in dB/20)*2^30) + * so create a lookup table for 2dB step + */ +static const unsigned int float_vol_db_lookup[] = { +0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, +0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, +0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, +0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, +0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, +0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, +0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, +0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, +0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, +0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +}; + struct tas2562_data { struct snd_soc_component *component; struct gpio_desc *sdz_gpio; @@ -34,6 +52,7 @@ struct tas2562_data { struct i2c_client *client; int v_sense_slot; int i_sense_slot; + int volume_lvl; };
static int tas2562_set_bias_level(struct snd_soc_component *component, @@ -334,6 +353,22 @@ static int tas2562_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret;
+ /* Set the Digital volume to -110dB */ + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, 0x00); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, 0x00); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, 0x0d); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, 0x43); + if (ret) + return ret; + + tas2562->volume_lvl = 0; + return 0; }
@@ -414,6 +449,50 @@ static int tas2562_dac_event(struct snd_soc_dapm_widget *w, return 0; }
+static int tas2562_volume_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = tas2562->volume_lvl; + return 0; +} + +static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + u32 reg_val; + + reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, + ((reg_val >> 16) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); + if (ret) + return ret; + + tas2562->volume_lvl = ucontrol->value.integer.value[0]; + + return ret; +} + +/* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(dvc_tlv, -11000, 100, 0); + static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0);
static const struct snd_kcontrol_new isense_switch = @@ -427,6 +506,17 @@ static const struct snd_kcontrol_new vsense_switch = static const struct snd_kcontrol_new tas2562_snd_controls[] = { SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0, tas2562_dac_tlv), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Volume Control", + .index = 0, + .tlv.p = dvc_tlv, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = tas2562_volume_control_get, + .put = tas2562_volume_control_put, + .private_value = SOC_SINGLE_VALUE(TAS2562_DVC_CFG1, 0, 110, 0, 0) , + }, };
static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { @@ -517,6 +607,10 @@ static const struct reg_default tas2562_reg_defaults[] = { { TAS2562_PB_CFG1, 0x20 }, { TAS2562_TDM_CFG0, 0x09 }, { TAS2562_TDM_CFG1, 0x02 }, + { TAS2562_DVC_CFG1, 0x40 }, + { TAS2562_DVC_CFG2, 0x40 }, + { TAS2562_DVC_CFG3, 0x00 }, + { TAS2562_DVC_CFG4, 0x00 }, };
static const struct regmap_config tas2562_regmap_config = { diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h index 6f55ebcf19ea..28e75fc431d0 100644 --- a/sound/soc/codecs/tas2562.h +++ b/sound/soc/codecs/tas2562.h @@ -35,8 +35,10 @@ #define TAS2562_REV_ID TAS2562_REG(0, 0x7d)
/* Page 2 */ -#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x01) -#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x02) +#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x0c) +#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x0d) +#define TAS2562_DVC_CFG3 TAS2562_REG(2, 0x0e) +#define TAS2562_DVC_CFG4 TAS2562_REG(2, 0x0f)
#define TAS2562_RESET BIT(0)
On Thu, Feb 20, 2020 at 11:27:21AM -0600, Dan Murphy wrote:
- /* Set the Digital volume to -110dB */
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, 0x00);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, 0x00);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, 0x0d);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, 0x43);
- if (ret)
return ret;
Is there a reason not to use the chip default here? Otherwise this looks good.
Mark
On 2/20/20 12:45 PM, Mark Brown wrote:
On Thu, Feb 20, 2020 at 11:27:21AM -0600, Dan Murphy wrote:
- /* Set the Digital volume to -110dB */
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, 0x00);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, 0x00);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, 0x0d);
- if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, 0x43);
- if (ret)
return ret;
Is there a reason not to use the chip default here? Otherwise this looks good.
Chip default is set to 0dB full blast+ 0x40400000. This sets the volume to -110dB.
I have the values backwards. CFG4 should b 0x43 and CFG3 should be 0x0d and CFG1&2 should be 0.
I will resend v2 with this fixed.
Dan
On Thu, Feb 20, 2020 at 12:46:57PM -0600, Dan Murphy wrote:
On 2/20/20 12:45 PM, Mark Brown wrote:
Is there a reason not to use the chip default here? Otherwise this looks good.
Chip default is set to 0dB full blast+ 0x40400000. This sets the volume to -110dB.
OK... that's a policy decision the same as all other volume changes and so shouldn't be done by the driver - as ever we don't know how the system is set up and what values make sense and keeping things out of the driver means we don't end up with competing system integration decisions causing changes in the driver. The system may have an external amplifier they prefer to use for hardware volume control, may prefer to do entirely soft volume control in their sound server or something like that.
Mark
On 2/20/20 1:18 PM, Mark Brown wrote:
On Thu, Feb 20, 2020 at 12:46:57PM -0600, Dan Murphy wrote:
On 2/20/20 12:45 PM, Mark Brown wrote:
Is there a reason not to use the chip default here? Otherwise this looks good.
Chip default is set to 0dB full blast+ 0x40400000. This sets the volume to -110dB.
OK... that's a policy decision the same as all other volume changes and so shouldn't be done by the driver - as ever we don't know how the system is set up and what values make sense and keeping things out of the driver means we don't end up with competing system integration decisions causing changes in the driver. The system may have an external amplifier they prefer to use for hardware volume control, may prefer to do entirely soft volume control in their sound server or something like that.
But this is an amplifier. Not sure why the system designer would design cascading amplifiers.
And if that was the case wouldn't you want the output to be low so you don't overdrive the ext amplifier front end?
I mean I have no qualms with removing the init from the driver. I will send v3 tomorrow after a 24 hour review cycle.
I was considering safety in that the device is on at full blast (not sure why the HW is defaulted that way but it is).
But if volume is adjusted prior to playback then this is not an issue. But if volume is not adjusted then it plays full blast.
Dan
On Thu, Feb 20, 2020 at 01:22:34PM -0600, Dan Murphy wrote:
On 2/20/20 1:18 PM, Mark Brown wrote:
decisions causing changes in the driver. The system may have an external amplifier they prefer to use for hardware volume control, may prefer to do entirely soft volume control in their sound server or something like that.
But this is an amplifier. Not sure why the system designer would design cascading amplifiers.
And if that was the case wouldn't you want the output to be low so you don't overdrive the ext amplifier front end?
The point is that we don't know what people are doing and should try to keep the kernel out of policy decisions unless there's something that's clearly just unconditionaly right for all systems. It's a lot easier to just have a clear rule that we defer to the wisdom of the silicon vendor than try to get into defaults, and it's a lot easier to just do this as consistently as we can rather than debating individual configuration changes.
I was considering safety in that the device is on at full blast (not sure why the HW is defaulted that way but it is).
I bet there's been issues with people turning things on and not realising they need register writes to hear anything, and the volume control here is a bit complex as well.
But if volume is adjusted prior to playback then this is not an issue. But if volume is not adjusted then it plays full blast.
Wouldn't be the first time. See all the dual purpose headphone/line outputs that people build, sensible defaults for headphones and line outputs are rather different.
Mark
On 2/20/20 1:40 PM, Mark Brown wrote:
On Thu, Feb 20, 2020 at 01:22:34PM -0600, Dan Murphy wrote:
On 2/20/20 1:18 PM, Mark Brown wrote:
decisions causing changes in the driver. The system may have an external amplifier they prefer to use for hardware volume control, may prefer to do entirely soft volume control in their sound server or something like that.
But this is an amplifier. Not sure why the system designer would design cascading amplifiers. And if that was the case wouldn't you want the output to be low so you don't overdrive the ext amplifier front end?
The point is that we don't know what people are doing and should try to keep the kernel out of policy decisions unless there's something that's clearly just unconditionaly right for all systems. It's a lot easier to just have a clear rule that we defer to the wisdom of the silicon vendor than try to get into defaults, and it's a lot easier to just do this as consistently as we can rather than debating individual configuration changes.
I completely agree on keeping policy decisions out of the kernel.
I was considering safety in that the device is on at full blast (not sure why the HW is defaulted that way but it is).
I bet there's been issues with people turning things on and not realising they need register writes to hear anything, and the volume control here is a bit complex as well.
Ack
But if volume is adjusted prior to playback then this is not an issue. But if volume is not adjusted then it plays full blast.
Wouldn't be the first time. See all the dual purpose headphone/line outputs that people build, sensible defaults for headphones and line outputs are rather different.
OK. I will remove them. Will do v3 tomorrow as v2 rearranged the values properly. V3 will remove them.
Dan
participants (2)
-
Dan Murphy
-
Mark Brown