[Sound-open-firmware] [PATCH 1/2] ASoC: SOF: set up volume tables with topology tlv data
This patch includes the changes to set up volume gain tables for kcontrols from topology tlv data.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@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"
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_ROOT_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);
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@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.
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_ROOT_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);
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@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);
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@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@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
participants (2)
-
Pierre-Louis Bossart
-
Ranjani Sridharan