Volume: Use min and max volume scale values from IPC

This patch adds feature to retrieve volume scale min and max from
IPC if the parameters are set. Volume min and max are now used
with ramp time parameter to compute gain ramp step to achieve
constant rate ramps with any volume scale. If the min and max are
zeros the previous FW operation is preserved.

Previously the firmware did not know the volume range so the
volume code uses in volume request a check to prevent exceeding
ramp length specified in topology by step size re-adjust. It was
done to prevent very long ramps to happen with volume scales with
large gain. However the smaller transitions remained longer than
necessary. This patch fixes that last remaining issue.

The ramp length check code is preserved to be compatible with
kernel versions those do not provide the min and max volume scale
values. When provided the check code has no impact.

The patch includes some cosmetic re-arranging of volume comp data
struct fields in addition to adding new the fields vol_ramp_range,
vol_min, and vol_max.

Also error traces were added to notify if the volume min/max or
request has been limited to fit to supported volume range.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This commit is contained in:
Seppo Ingalsuo 2019-06-05 14:10:41 +03:00 committed by Tomasz Lauda
parent a2fa79f211
commit 6f65414176
2 changed files with 73 additions and 13 deletions

View File

@ -83,7 +83,7 @@ static uint64_t vol_work(void *data)
vol += cd->ramp_increment[i];
if (cd->volume[i] < cd->tvolume[i]) {
/* ramp up, check if ramp completed */
if (vol >= cd->tvolume[i] || vol >= VOL_MAX) {
if (vol >= cd->tvolume[i] || vol >= cd->vol_max) {
vol_update(cd, i);
} else {
cd->volume[i] = vol;
@ -96,7 +96,8 @@ static uint64_t vol_work(void *data)
vol_update(cd, i);
} else {
/* ramp completed ? */
if (vol <= cd->tvolume[i] || vol <= VOL_MIN) {
if (vol <= cd->tvolume[i] ||
vol <= cd->vol_min) {
vol_update(cd, i);
} else {
cd->volume[i] = vol;
@ -157,14 +158,53 @@ static struct comp_dev *volume_new(struct sof_ipc_comp *comp)
schedule_task_init(&cd->volwork, SOF_SCHEDULE_LL, SOF_TASK_PRI_MED,
vol_work, dev, 0, 0);
/* set the default volumes */
/* Set the default volumes. If IPC sets min_value or max_value to
* not-zero, use them. Otherwise set to internal limits and notify
* ramp step calculation about assumed range with the range set to
* zero.
*/
if (vol->min_value || vol->max_value) {
if (vol->min_value < VOL_MIN) {
/* Use VOL_MIN instead, no need to stop new(). */
cd->vol_min = VOL_MIN;
trace_volume_error("volume_new(): vol->min_value "
"was limited to VOL_MIN.");
} else {
cd->vol_min = vol->min_value;
}
if (vol->max_value > VOL_MAX) {
/* Use VOL_MAX instead, no need to stop new(). */
cd->vol_max = VOL_MAX;
trace_volume_error("volume_new(): vol->max_value "
"was limited to VOL_MAX.");
} else {
cd->vol_max = vol->max_value;
}
cd->vol_ramp_range = vol->max_value - vol->min_value;
} else {
/* Legacy mode, set the limits to firmware capability where
* VOL_MAX is a very large gain to avoid restricting valid
* requests. The default ramp rate will be computed based
* on 0 - 1.0 gain range assumption when vol_ramp_range
* is set to 0.
*/
cd->vol_min = VOL_MIN;
cd->vol_max = VOL_MAX;
cd->vol_ramp_range = 0;
}
for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) {
cd->volume[i] = MAX(MIN(VOL_MAX, VOL_ZERO_DB), VOL_MIN);
cd->volume[i] = MAX(MIN(cd->vol_max, VOL_ZERO_DB),
cd->vol_min);
cd->tvolume[i] = cd->volume[i];
}
trace_volume("vol->initial_ramp = %d, vol->ramp = %d",
vol->initial_ramp, vol->ramp);
trace_volume("vol->initial_ramp = %d, vol->ramp = %d, "
"vol->min_value = %d, vol->max_value = %d",
vol->initial_ramp, vol->ramp,
vol->min_value, vol->max_value);
dev->state = COMP_STATE_READY;
return dev;
@ -220,11 +260,19 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, uint32_t vol)
* multiplication overflow with the 32 bit value. Non-zero MIN option
* can be useful to prevent totally muted small volume gain.
*/
if (v <= VOL_MIN)
if (v <= VOL_MIN) {
/* No need to fail, just trace the event. */
trace_volume_error("volume_set_chan: Limited request %d to "
"VOL_MIN.", v);
v = VOL_MIN;
}
if (v > VOL_MAX)
if (v > VOL_MAX) {
/* No need to fail, just trace the event. */
trace_volume_error("volume_set_chan: Limited request %d to "
"VOL_MAX.", v);
v = VOL_MAX;
}
cd->tvolume[chan] = v;
@ -242,6 +290,15 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, uint32_t vol)
else
inc = VOL_ZERO_DB;
/* Scale the increment with actual volume range if it was
* passed via IPC.
*/
if (cd->vol_ramp_range)
inc = q_multsr_32x32(inc, cd->vol_ramp_range,
Q_SHIFT_BITS_32(VOL_QXY_Y,
VOL_QXY_Y,
VOL_QXY_Y));
delta = cd->tvolume[chan] - cd->volume[chan];
delta_abs = ABS(delta);

View File

@ -85,17 +85,20 @@
* Gain amplitude value is between 0 (mute) ... 2^16 (0dB) ... 2^24 (~+48dB).
*/
struct comp_data {
enum sof_ipc_frame source_format; /**< source frame format */
enum sof_ipc_frame sink_format; /**< sink frame format */
struct task volwork; /**< volume scheduled work function */
struct sof_ipc_ctrl_value_chan *hvol; /**< host volume readback */
int32_t volume[SOF_IPC_MAX_CHANNELS]; /**< current volume */
int32_t tvolume[SOF_IPC_MAX_CHANNELS]; /**< target volume */
int32_t mvolume[SOF_IPC_MAX_CHANNELS]; /**< mute volume */
int32_t ramp_increment[SOF_IPC_MAX_CHANNELS]; /**< for linear ramp */
int32_t vol_min; /**< minimum volume */
int32_t vol_max; /**< maximum volume */
int32_t vol_ramp_range; /**< max ramp transition */
enum sof_ipc_frame source_format; /**< source frame format */
enum sof_ipc_frame sink_format; /**< sink frame format */
/**< volume processing function */
void (*scale_vol)(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames);
struct task volwork; /**< volume scheduled work function */
struct sof_ipc_ctrl_value_chan *hvol; /**< host volume readback */
int32_t ramp_increment[SOF_IPC_MAX_CHANNELS]; /**< for linear ramp */
};
/** \brief Volume processing functions map. */