Unverified Commit 1b0f3f9e authored by Shuming Fan's avatar Shuming Fan Committed by Mark Brown
Browse files

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: default avatarShuming Fan <shumingf@realtek.com>
Reviewed-by: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20251106093335.1363237-1-shumingf@realtek.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6d34b66f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+9 −25
Original line number Diff line number Diff line
@@ -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;

	step = (step * 100) >> 8;

	mc->min = min >> shift;
	mc->max = max >> shift;
	mc->shift = shift;
	mc->rshift = shift;
	mc->sign_bit = 15 - shift;
	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;
+51 −11
Original line number Diff line number Diff line
@@ -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;