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>
Link: https://lore.kernel.org/r/20200221124151.8774-1-dmurphy@ti.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Dan Murphy 2020-02-21 06:41:51 -06:00 committed by Mark Brown
parent 150cbf8e66
commit bf726b1c86
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 82 additions and 2 deletions

View File

@ -26,6 +26,24 @@
#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ #define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FORMAT_S32_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 tas2562_data {
struct snd_soc_component *component; struct snd_soc_component *component;
struct gpio_desc *sdz_gpio; struct gpio_desc *sdz_gpio;
@ -34,6 +52,7 @@ struct tas2562_data {
struct i2c_client *client; struct i2c_client *client;
int v_sense_slot; int v_sense_slot;
int i_sense_slot; int i_sense_slot;
int volume_lvl;
}; };
static int tas2562_set_bias_level(struct snd_soc_component *component, static int tas2562_set_bias_level(struct snd_soc_component *component,
@ -413,6 +432,50 @@ static int tas2562_dac_event(struct snd_soc_dapm_widget *w,
return 0; 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 DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0);
static const struct snd_kcontrol_new isense_switch = static const struct snd_kcontrol_new isense_switch =
@ -426,6 +489,17 @@ static const struct snd_kcontrol_new vsense_switch =
static const struct snd_kcontrol_new tas2562_snd_controls[] = { static const struct snd_kcontrol_new tas2562_snd_controls[] = {
SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0, SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0,
tas2562_dac_tlv), 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[] = { static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = {
@ -516,6 +590,10 @@ static const struct reg_default tas2562_reg_defaults[] = {
{ TAS2562_PB_CFG1, 0x20 }, { TAS2562_PB_CFG1, 0x20 },
{ TAS2562_TDM_CFG0, 0x09 }, { TAS2562_TDM_CFG0, 0x09 },
{ TAS2562_TDM_CFG1, 0x02 }, { 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 = { static const struct regmap_config tas2562_regmap_config = {

View File

@ -35,8 +35,10 @@
#define TAS2562_REV_ID TAS2562_REG(0, 0x7d) #define TAS2562_REV_ID TAS2562_REG(0, 0x7d)
/* Page 2 */ /* Page 2 */
#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x01) #define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x0c)
#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x02) #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) #define TAS2562_RESET BIT(0)