ASoC: tas2562: Add support for digital volume control
diff mbox series

Message ID 20200220172721.10547-1-dmurphy@ti.com
State New
Headers show
Series
  • ASoC: tas2562: Add support for digital volume control
Related show

Commit Message

Dan Murphy Feb. 20, 2020, 5:27 p.m. UTC
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(-)

Comments

Mark Brown Feb. 20, 2020, 6:45 p.m. UTC | #1
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.
Dan Murphy Feb. 20, 2020, 6:46 p.m. UTC | #2
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
Mark Brown Feb. 20, 2020, 7:18 p.m. UTC | #3
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.
Dan Murphy Feb. 20, 2020, 7:22 p.m. UTC | #4
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
Mark Brown Feb. 20, 2020, 7:40 p.m. UTC | #5
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.
Dan Murphy Feb. 20, 2020, 7:52 p.m. UTC | #6
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

Patch
diff mbox series

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)