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:
Shuming Fan
2025-11-06 17:33:35 +08:00
committed by Mark Brown
parent 6d34b66fb7
commit 1b0f3f9ee4
3 changed files with 61 additions and 36 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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;