mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
ASoC: SDCA: support Q7.8 volume format
The SDCA specification uses Q7.8 volume format. This patch adds a field to indicate whether it is SDCA volume control and supports the volume settings. Signed-off-by: Shuming Fan <shumingf@realtek.com> Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20251106093335.1363237-1-shumingf@realtek.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
@@ -1225,6 +1225,7 @@ struct soc_mixer_control {
|
||||
unsigned int sign_bit;
|
||||
unsigned int invert:1;
|
||||
unsigned int autodisable:1;
|
||||
unsigned int sdca_q78:1;
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj;
|
||||
#endif
|
||||
|
||||
@@ -795,7 +795,6 @@ static int control_limit_kctl(struct device *dev,
|
||||
struct sdca_control_range *range;
|
||||
int min, max, step;
|
||||
unsigned int *tlv;
|
||||
int shift;
|
||||
|
||||
if (control->type != SDCA_CTL_DATATYPE_Q7P8DB)
|
||||
return 0;
|
||||
@@ -814,37 +813,22 @@ static int control_limit_kctl(struct device *dev,
|
||||
min = sign_extend32(min, control->nbits - 1);
|
||||
max = sign_extend32(max, control->nbits - 1);
|
||||
|
||||
/*
|
||||
* FIXME: Only support power of 2 step sizes as this can be supported
|
||||
* by a simple shift.
|
||||
*/
|
||||
if (hweight32(step) != 1) {
|
||||
dev_err(dev, "%s: %s: currently unsupported step size\n",
|
||||
entity->label, control->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SDCA volumes are in steps of 1/256th of a dB, a step down of
|
||||
* 64 (shift of 6) gives 1/4dB. 1/4dB is the smallest unit that is also
|
||||
* representable in the ALSA TLVs which are in 1/100ths of a dB.
|
||||
*/
|
||||
shift = max(ffs(step) - 1, 6);
|
||||
|
||||
tlv = devm_kcalloc(dev, 4, sizeof(*tlv), GFP_KERNEL);
|
||||
if (!tlv)
|
||||
return -ENOMEM;
|
||||
|
||||
tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
|
||||
tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
|
||||
tlv[1] = 2 * sizeof(*tlv);
|
||||
tlv[2] = (min * 100) >> 8;
|
||||
tlv[3] = ((1 << shift) * 100) >> 8;
|
||||
tlv[3] = (max * 100) >> 8;
|
||||
|
||||
mc->min = min >> shift;
|
||||
mc->max = max >> shift;
|
||||
mc->shift = shift;
|
||||
mc->rshift = shift;
|
||||
mc->sign_bit = 15 - shift;
|
||||
step = (step * 100) >> 8;
|
||||
|
||||
mc->min = ((int)tlv[2] / step);
|
||||
mc->max = ((int)tlv[3] / step);
|
||||
mc->shift = step;
|
||||
mc->sign_bit = 15;
|
||||
mc->sdca_q78 = 1;
|
||||
|
||||
kctl->tlv.p = tlv;
|
||||
kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
|
||||
@@ -110,6 +110,36 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
|
||||
|
||||
static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val,
|
||||
unsigned int mask, unsigned int shift, int max)
|
||||
{
|
||||
int val = reg_val;
|
||||
|
||||
if (WARN_ON(!mc->shift))
|
||||
return -EINVAL;
|
||||
|
||||
val = sign_extend32(val, mc->sign_bit);
|
||||
val = (((val * 100) >> 8) / (int)mc->shift);
|
||||
val -= mc->min;
|
||||
|
||||
return val & mask;
|
||||
}
|
||||
|
||||
static unsigned int sdca_soc_q78_ctl_to_reg(struct soc_mixer_control *mc, int val,
|
||||
unsigned int mask, unsigned int shift, int max)
|
||||
{
|
||||
unsigned int ret_val;
|
||||
int reg_val;
|
||||
|
||||
if (WARN_ON(!mc->shift))
|
||||
return -EINVAL;
|
||||
|
||||
reg_val = val + mc->min;
|
||||
ret_val = (int)((reg_val * mc->shift) << 8) / 100;
|
||||
|
||||
return ret_val & mask;
|
||||
}
|
||||
|
||||
static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val,
|
||||
unsigned int mask, unsigned int shift, int max)
|
||||
{
|
||||
@@ -197,19 +227,27 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc, int mask, int max)
|
||||
{
|
||||
unsigned int (*ctl_to_reg)(struct soc_mixer_control *, int, unsigned int, unsigned int, int);
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val1, val_mask;
|
||||
unsigned int val2 = 0;
|
||||
bool double_r = false;
|
||||
int ret;
|
||||
|
||||
if (mc->sdca_q78) {
|
||||
ctl_to_reg = sdca_soc_q78_ctl_to_reg;
|
||||
val_mask = mask;
|
||||
} else {
|
||||
ctl_to_reg = soc_mixer_ctl_to_reg;
|
||||
val_mask = mask << mc->shift;
|
||||
}
|
||||
|
||||
ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[0], max);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val1 = soc_mixer_ctl_to_reg(mc, ucontrol->value.integer.value[0],
|
||||
val1 = ctl_to_reg(mc, ucontrol->value.integer.value[0],
|
||||
mask, mc->shift, max);
|
||||
val_mask = mask << mc->shift;
|
||||
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[1], max);
|
||||
@@ -217,14 +255,10 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
return ret;
|
||||
|
||||
if (mc->reg == mc->rreg) {
|
||||
val1 |= soc_mixer_ctl_to_reg(mc,
|
||||
ucontrol->value.integer.value[1],
|
||||
mask, mc->rshift, max);
|
||||
val1 |= ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->rshift, max);
|
||||
val_mask |= mask << mc->rshift;
|
||||
} else {
|
||||
val2 = soc_mixer_ctl_to_reg(mc,
|
||||
ucontrol->value.integer.value[1],
|
||||
mask, mc->shift, max);
|
||||
val2 = ctl_to_reg(mc, ucontrol->value.integer.value[1], mask, mc->shift, max);
|
||||
double_r = true;
|
||||
}
|
||||
}
|
||||
@@ -248,21 +282,27 @@ static int soc_get_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
struct soc_mixer_control *mc, int mask, int max)
|
||||
{
|
||||
int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, unsigned int, int);
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int reg_val;
|
||||
int val;
|
||||
|
||||
if (mc->sdca_q78)
|
||||
reg_to_ctl = sdca_soc_q78_reg_to_ctl;
|
||||
else
|
||||
reg_to_ctl = soc_mixer_reg_to_ctl;
|
||||
|
||||
reg_val = snd_soc_component_read(component, mc->reg);
|
||||
val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max);
|
||||
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max);
|
||||
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
if (mc->reg == mc->rreg) {
|
||||
val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->rshift, max);
|
||||
val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max);
|
||||
} else {
|
||||
reg_val = snd_soc_component_read(component, mc->rreg);
|
||||
val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max);
|
||||
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max);
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[1] = val;
|
||||
|
||||
Reference in New Issue
Block a user