ASoC: tfa987x: add volume control command

TFA9871/74 doesnt provide a linear digital volume control, it has 2 levels,
the first is I2S Input Level(INPLEV) in Register 02H,it can be set 6dB or
0dB(default). The second is I2S gain attenuation(TDMSPKG) in Register 61H,
it's the gain of the Class-D output can be amplified btw +6dB and +21dB,
contains 16 steps.

Change-Id: I64f0426cac3829b0419c2e52358b043e749f2625
Signed-off-by: zhouwl <zhouwl@lenovo.com>
Reviewed-on: https://gerrit.mot.com/1237458
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Hujun Liao <liaohj@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
zhouwl 2018-09-04 15:31:18 +08:00 • committed by Wanlong Zhou
commit 1d1e2644b5

View file

@ -57,6 +57,11 @@
#define TF98XX_MAX_DSP_START_TRY_COUNT 10
#define TFA98XX_SYS_CTRL 0x02
#define TFA98XX_SYS_CTRL_INPLEV_MASK 0xFFEF /* bit4: INPLEV: 0 = 0dB, 1 = -6dB */
#define TFA98XX_GAIN_ATT 0x61
#define TFA98XX_GAIN_ATT_TDMSPKG_MASK 0xFC3F /* bit9:6 TDMSPKG: 0000 = 21dB,..., 1111 = 5.9dB */
/* data accessible by all instances */
static struct kmem_cache *tfa98xx_cache = NULL; /* Memory pool used for DSP messages */
/* Mutex protected data */
@ -1597,9 +1602,108 @@ static int tfa98xx_get_stereo_ctl(struct snd_kcontrol *kcontrol,
return 0;
}
#endif
static int tfa98xx_info_volume_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0xFF;
return 0;
}
static int tfa98xx_set_volume_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tfa98xx *tfa98xx;
int volume, value1, value2, ret;
volume = (int)ucontrol->value.integer.value[0]/8;
mutex_lock(&tfa98xx_mutex);
list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) {
ret = regmap_read(tfa98xx->regmap, TFA98XX_SYS_CTRL, &value1);
if (ret == 0) {
ret = regmap_read(tfa98xx->regmap, TFA98XX_GAIN_ATT, &value2);
}
if (ret < 0) {
pr_err("%s, failed to read register: %d\n", __func__, ret);
return Tfa98xx_Error_Fail;
}
if (volume > 25) {
value1 = (value1 & TFA98XX_SYS_CTRL_INPLEV_MASK) | (1 << 4);
value2 = (value2 & TFA98XX_GAIN_ATT_TDMSPKG_MASK) | ((volume-16) << 6);
} else if (volume < 16) {
value1 = (value1 & TFA98XX_SYS_CTRL_INPLEV_MASK) | (0 << 4);
value2 = (value2 & TFA98XX_GAIN_ATT_TDMSPKG_MASK) | (volume << 6);
}
ret = regmap_write(tfa98xx->regmap, TFA98XX_SYS_CTRL, value1);
if (ret == 0) {
ret = regmap_write(tfa98xx->regmap, TFA98XX_GAIN_ATT, value2);
}
if (ret < 0) {
pr_err("%s, failed to write register: %d\n", __func__, ret);
return Tfa98xx_Error_Fail;
}
}
mutex_unlock(&tfa98xx_mutex);
return 1;
}
static int tfa98xx_get_volume_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tfa98xx *tfa98xx;
int volume, value1, value2, ret;
mutex_lock(&tfa98xx_mutex);
list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) {
ret = regmap_read(tfa98xx->regmap, TFA98XX_SYS_CTRL, &value1);
if (ret == 0) {
ret = regmap_read(tfa98xx->regmap, TFA98XX_GAIN_ATT, &value2);
}
if (ret < 0) {
pr_err("%s, failed to read register: %d\n", __func__, ret);
return Tfa98xx_Error_Fail;
}
value1 = (value1 & ~(TFA98XX_SYS_CTRL_INPLEV_MASK)) >> 4;
value2 = (value2 & ~(TFA98XX_GAIN_ATT_TDMSPKG_MASK)) >> 6;
if (value1 == 1)
volume = (value2 + 16) * 8;
else
volume = value2 * 8;
ucontrol->value.integer.value[0] = volume;
}
mutex_unlock(&tfa98xx_mutex);
return 0;
}
static int tfa98xx_info_mode_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
return 0;
}
static int tfa98xx_set_mode_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 1;
}
static int tfa98xx_get_mode_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static int tfa98xx_create_controls(struct tfa98xx *tfa98xx)
{
@ -1739,6 +1843,20 @@ static int tfa98xx_create_controls(struct tfa98xx *tfa98xx)
mix_index++;
#endif
tfa98xx_controls[mix_index].name = "NXP Volume";
tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
tfa98xx_controls[mix_index].info = tfa98xx_info_volume_ctl;
tfa98xx_controls[mix_index].get = tfa98xx_get_volume_ctl;
tfa98xx_controls[mix_index].put = tfa98xx_set_volume_ctl;
mix_index++;
tfa98xx_controls[mix_index].name = "NXP Mode";
tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
tfa98xx_controls[mix_index].info = tfa98xx_info_mode_ctl;
tfa98xx_controls[mix_index].get = tfa98xx_get_mode_ctl;
tfa98xx_controls[mix_index].put = tfa98xx_set_mode_ctl;
mix_index++;
ret = snd_soc_add_codec_controls(tfa98xx->codec,
tfa98xx_controls, mix_index);
@ -1938,15 +2056,10 @@ retry:
}
return Tfa98xx_Error_Fail;
}
if (1)
if (tfa98xx_ftrace_regs)
dev_err(&tfa98xx->i2c->dev, " WR reg=0x%02x, val=0x%04x %s\n",
subaddress, value,
ret<0? "Error!!" : "");
if (tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\tWR reg=0x%02x, val=0x%04x %s\n",
subaddress, value,
ret<0? "Error!!" : "");
return error;
}
@ -1983,14 +2096,10 @@ retry:
}
*val = value & 0xffff;
if (1)
if (tfa98xx_ftrace_regs)
dev_err(&tfa98xx->i2c->dev, "RD reg=0x%02x, val=0x%04x %s\n",
subaddress, *val,
ret<0? "Error!!" : "");
if (tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\tRD reg=0x%02x, val=0x%04x %s\n",
subaddress, *val,
ret<0? "Error!!" : "");
return error;
}
@ -3399,7 +3508,7 @@ static int tfa98xx_i2c_probe(struct i2c_client *i2c,
tfa98xx_device_count++;
list_add(&tfa98xx->list, &tfa98xx_device_list);
mutex_unlock(&tfa98xx_mutex);
return 0;
}