[Sound-open-firmware] [PATCH 1/2] ASoC: SOF: set up volume tables with topology tlv data

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Tue May 15 15:12:09 CEST 2018



On 05/14/2018 07:30 PM, Ranjani Sridharan wrote:
> 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
Ah ok. will cherry-pick this one then, thanks for the precision. We 
typically follow Mark's for-next branch, except when we don't ;-)
>>>       
>>>       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);
>>>
>>
> _______________________________________________
> Sound-open-firmware mailing list
> Sound-open-firmware at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware



More information about the Sound-open-firmware mailing list