[Sound-open-firmware] [PATCH 1/2] ASoC: SOF: set up volume tables with topology tlv data
Ranjani Sridharan
ranjani.sridharan at linux.intel.com
Tue May 15 02:30:55 CEST 2018
On Mon, 2018-05-14 at 18:09 -0500, Pierre-Louis Bossart wrote:
> On 5/14/18 2:05 PM, Ranjani Sridharan wrote:
> > This patch includes the changes to set up volume gain tables for
> > kcontrols from topology tlv data.
> >
> > Signed-off-by: Ranjani Sridharan <ranjani.sridharan at linux.intel.com
> > >
> > ---
> >
> > Notes:
> > This patch depends on the patch upstreamed to the for-next
> > branch
> > with commit id: 08f9f4485f2158de0fa77506687a073cb869e803
> > and titled "ALSA: core api: define offsets for TLV items"
>
> Ranjani, has this patch been merged by Mark? I don't see it in his
> tree
> and the commit ID is not valid.
Pierre, This patch has been merged by Takashi. I picked the commit id
from his for-next branch. 08f9f4485f2158de0fa77506687a073cb869e803
>
> >
> > Tested on:
> > Minnowboard turbot with RT5651
> > kernel: https://github.com/plbossart/sound branch:
> > topic/sof-v4.14
> > commit: b433999058d8a8dee5d37c4176d94257ab269b71
> > SOF: master
> > SOFT: master
> >
> > sound/soc/sof/sof-priv.h | 1 +
> > sound/soc/sof/topology.c | 181
> > ++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 181 insertions(+), 1 deletion(-)
> >
> > diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
> > index 0f9b659461d9..bde985f6f438 100644
> > --- a/sound/soc/sof/sof-priv.h
> > +++ b/sound/soc/sof/sof-priv.h
> > @@ -185,6 +185,7 @@ struct snd_sof_control {
> > struct sof_ipc_ctrl_data *control_data;
> > u32 size; /* cdata size */
> > enum sof_ipc_ctrl_cmd cmd;
> > + u32 *volume_table; /* volume table computed from tlv
> > data*/
> >
> > struct mutex mutex;
> > struct list_head list; /* list in sdev control
> > list */
> > diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
> > index 3d0d71af3d37..cf5609a52f36 100644
> > --- a/sound/soc/sof/topology.c
> > +++ b/sound/soc/sof/topology.c
> > @@ -20,11 +20,162 @@
> > #include <linux/string.h>
> > #include <sound/soc-topology.h>
> > #include <sound/soc.h>
> > +#include <uapi/sound/tlv.h>
> > +#include <sound/tlv.h>
> > #include <uapi/sound/sof-ipc.h>
> > #include <uapi/sound/sof-topology.h>
> > #include "sof-priv.h"
> >
> > #define COMP_ID_UNASSIGNED 0xffffffff
> > +/* Constants used in the computation of linear volume gain from dB
> > gain */
> > +/* 20th root of 10 in Q1.16 fixed-point notation*/
> > +#define VOL_TWENTIETH_ROOT_OF_TEN 73533
> > +/* 40th root of 10 in Q1.16 fixed-point notation*/
> > +#define VOL_FORTIETH_ROOT_OF_TEN 69419
> > +/* Volume fractional word length */
> > +#define VOLUME_FWL 16
> > +/* 0.5 dB step value in topology TLV */
> > +#define VOL_HALF_DB_STEP 50
> > +
> > +/* TLV data items */
> > +#define TLV_ITEMS 3
> > +#define TLV_MIN 0
> > +#define TLV_STEP 1
> > +#define TLV_MUTE 2
> > +
> > +static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS])
> > +{
> > + /* we only support dB scale TLV type at the moment */
> > + if ((int)p[SNDRV_CTL_TLVO_TYPE] !=
> > SNDRV_CTL_TLVT_DB_SCALE)
> > + return -EINVAL;
> > +
> > + /* min value in topology tlv data is multiplied by 100 */
> > + tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100;
> > +
> > + /* volume steps */
> > + tlv[TLV_STEP] =
> > (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] &
> > + TLV_DB_SCALE_MASK);
> > +
> > + /* mute ON/OFF */
> > + if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] &
> > + TLV_DB_SCALE_MUTE) == 0)
> > + tlv[TLV_MUTE] = 0;
> > + else
> > + tlv[TLV_MUTE] = 1;
> > +
> > + return 0;
> > +}
> > +
> > +/* Function to truncate an unsigned 64-bit number
> > + * by x bits and return 32-bit unsigned number
> > + * This function also takes care of rounding while truncating
> > + */
> > +static inline u32 vol_shift_64(u64 i, u32 x)
> > +{
> > + /* do not truncate more than 32 bits */
> > + if (x > 32)
> > + x = 32;
> > +
> > + if (x == 0)
> > + return (u32)i;
> > +
> > + return (u32)(((i >> (x - 1)) + 1) >> 1);
> > +}
> > +
> > +/* Function to compute a ^ exp where,
> > + * a is a fractional number represented by a fixed-point integer
> > + * with a fractional world length of "fwl"
> > + * exp is an integer
> > + * fwl is the fractional word length
> > + * Return value is a fractional number represented by a fixed-
> > point
> > + * integer with a fractional word length of "fwl"
> > + */
> > +static u32 vol_pow32(u32 a, int exp, u32 fwl)
> > +{
> > + int i, iter;
> > + u32 power = 1 << fwl;
> > +
> > + /* if exponent is 0, return 1 */
> > + if (exp == 0)
> > + return power;
> > +
> > + /* determine the number of iterations based on the
> > exponent */
> > + if (exp < 0)
> > + iter = exp * -1;
> > + else
> > + iter = exp;
> > +
> > + /* mutiply a "iter" times to compute power */
> > + for (i = 0; i < iter; i++) {
> > + /* Product of 2 Qx.fwl fixed-point numbers yields
> > a Q2*x.2*fwl
> > + * Truncate product back to fwl fractional bits
> > with rounding
> > + */
> > + power = vol_shift_64((u64)power * a, fwl);
> > + }
> > +
> > + if (exp > 0) {
> > + /* if exp is positive, return the result */
> > + return power;
> > + }
> > +
> > + /* if exp is negative, return the multiplicative inverse
> > */
> > + return (u32)((u64)(1 << fwl) * (1 << fwl) / power);
> > +}
> > +
> > +/* Function to calculate volume gain from TLV data
> > + * This function can only handle gain steps that are multiples of
> > 0.5 dB
> > + */
> > +static u32 vol_compute_gain(u32 value, int *tlv)
> > +{
> > + int dB_gain;
> > + u32 linear_gain;
> > + int f_step;
> > +
> > + /* mute volume */
> > + if (value == 0 && tlv[TLV_MUTE])
> > + return 0;
> > +
> > + /* compute dB gain from tlv
> > + * tlv_step in topology is multiplied by 100
> > + */
> > + dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100;
> > +
> > + /* compute linear gain
> > + * represented by fixed-point int with VOLUME_FWL
> > fractional bits
> > + */
> > + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN,
> > dB_gain, VOLUME_FWL);
> > +
> > + /* extract the fractional part of volume step */
> > + f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100);
> > +
> > + /* if volume step is an odd multiple of 0.5 dB */
> > + if (f_step == VOL_HALF_DB_STEP && (value & 1))
> > + linear_gain = vol_shift_64((u64)linear_gain *
> > + VOL_FORTIETH_ROO
> > T_OF_TEN,
> > + VOLUME_FWL);
> > +
> > + return linear_gain;
> > +}
> > +
> > +/* Set up volume table for kcontrols from tlv data
> > + * "size" specifies the number of entries in the table
> > + */
> > +static int set_up_volume_table(struct snd_sof_control *scontrol,
> > + int tlv[TLV_ITEMS], int size)
> > +{
> > + int j;
> > +
> > + /* init the volume table */
> > + scontrol->volume_table = kcalloc(size, sizeof(u32),
> > GFP_KERNEL);
> > + if (!scontrol->volume_table)
> > + return -ENOMEM;
> > +
> > + /* populate the volume table */
> > + for (j = 0; j < size ; j++)
> > + scontrol->volume_table[j] = vol_compute_gain(j,
> > tlv);
> > +
> > + return 0;
> > +}
> >
> > struct sof_dai_types {
> > const char *name;
> > @@ -92,6 +243,7 @@ static int sof_control_load_volume(struct
> > snd_soc_component *scomp,
> > if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN)
> > return -EINVAL;
> >
> > +
> > /* init the volume get/put data */
> > scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
> > sizeof(struct sof_ipc_ctrl_value_chan) * mc-
> > >num_channels;
> > @@ -790,7 +942,12 @@ static int sof_widget_load_pga(struct
> > snd_soc_component *scomp, int index,
> > struct snd_sof_dev *sdev =
> > snd_soc_component_get_drvdata(scomp);
> > struct snd_soc_tplg_private *private = &tw->priv;
> > struct sof_ipc_comp_volume volume;
> > - int ret;
> > + struct snd_soc_dapm_widget *widget = swidget->widget;
> > + const struct snd_kcontrol_new *kc = NULL;
> > + struct soc_mixer_control *sm;
> > + struct snd_sof_control *scontrol;
> > + const unsigned int *p;
> > + int ret, tlv[TLV_ITEMS];
> >
> > if (tw->num_kcontrols != 1) {
> > dev_err(sdev->dev, "error: invalid kcontrol count
> > %d for volume\n",
> > @@ -798,6 +955,28 @@ static int sof_widget_load_pga(struct
> > snd_soc_component *scomp, int index,
> > return -EINVAL;
> > }
> >
> > + /* set up volume gain tables for kcontrol */
> > + kc = &widget->kcontrol_news[0];
> > + sm = (struct soc_mixer_control *)kc->private_value;
> > +
> > + /* get volume control */
> > + scontrol = sm->dobj.private;
> > +
> > + /* get topology tlv data */
> > + p = kc->tlv.p;
> > +
> > + /* extract tlv data */
> > + if (get_tlv_data(p, tlv) < 0) {
> > + dev_err(sdev->dev, "error: invalid TLV data\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* set up volume table */
> > + if (set_up_volume_table(scontrol, tlv, sm->max) < 0) {
> > + dev_err(sdev->dev, "error: setting up volume
> > table\n");
> > + return -ENOMEM;
> > + }
> > +
> > /* configure dai IPC message */
> > memset(&volume, 0, sizeof(volume));
> > volume.comp.hdr.size = sizeof(volume);
> >
>
>
More information about the Sound-open-firmware
mailing list