blob: 4b1518e404f1a841724c31ae1304507ca6521bf5 [file] [log] [blame]
/*
* Copyright (c) 2018, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Intel Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Tomasz Lauda <tomasz.lauda@linux.intel.com>
*/
/**
* \file audio/volume_hifi3.c
* \brief Volume HiFi3 processing implementation
* \authors Tomasz Lauda <tomasz.lauda@linux.intel.com>
*/
#include "volume.h"
#if defined(__XCC__) && XCHAL_HAVE_HIFI3
#include <xtensa/tie/xt_hifi3.h>
/** \brief Volume scale ratio. */
#define VOL_SCALE (uint32_t)((double)INT32_MAX / VOL_MAX)
/**
* \brief HiFi3 enabled volume processing from 16 bit to 16 bit.
* \param[in,out] dev Volume base component device.
* \param[in,out] sink Destination buffer.
* \param[in,out] source Source buffer.
*/
static void vol_s16_to_s16(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source)
{
struct comp_data *cd = comp_get_drvdata(dev);
uint32_t vol_scaled[SOF_IPC_MAX_CHANNELS];
ae_f32x2 volume;
ae_f32x2 mult;
ae_f32x2 out_sample;
ae_f16x4 in_sample = AE_ZERO16();
size_t channel;
int i;
ae_int16 *in = (ae_int16 *)source->r_ptr;
ae_int16 *out = (ae_int16 *)sink->w_ptr;
/* Scale to VOL_MAX */
for (channel = 0; channel < dev->params.channels; channel++)
vol_scaled[channel] = cd->volume[channel] * VOL_SCALE;
/* Main processing loop */
for (i = 0; i < dev->frames; i++) {
/* Processing per channel */
for (channel = 0; channel < dev->params.channels; channel++) {
/* Load the input sample */
AE_L16_XP(in_sample, in, sizeof(ae_int16));
/* Get gain coefficients */
volume = *((ae_f32 *)&vol_scaled[channel]);
/* Multiply the input sample */
mult = AE_MULFP32X16X2RS_L(volume, in_sample);
/* Shift right and round to get 16 in 32 bits */
out_sample = AE_SRAA32RS(mult, 16);
/* Store the output sample */
AE_S16_0_XP(AE_MOVF16X4_FROMF32X2(out_sample),
out, sizeof(ae_int16));
}
}
}
/**
* \brief HiFi3 enabled volume processing from 16 bit to x bit.
* \param[in,out] dev Volume base component device.
* \param[in,out] sink Destination buffer.
* \param[in,out] source Source buffer.
*/
static void vol_s16_to_sX(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source)
{
struct comp_data *cd = comp_get_drvdata(dev);
uint32_t vol_scaled[SOF_IPC_MAX_CHANNELS];
ae_f32x2 volume;
ae_f32x2 mult;
ae_f32x2 out_sample;
ae_f16x4 in_sample = AE_ZERO16();
size_t channel;
uint8_t shift_left = 0;
int i;
ae_int16 *in = (ae_int16 *)source->r_ptr;
ae_int32 *out = (ae_int32 *)sink->w_ptr;
/* Get value of shift left */
if (cd->sink_format == SOF_IPC_FRAME_S24_4LE)
shift_left = 8;
else if (cd->sink_format == SOF_IPC_FRAME_S32_LE)
shift_left = 16;
/* Scale to VOL_MAX */
for (channel = 0; channel < dev->params.channels; channel++)
vol_scaled[channel] = cd->volume[channel] * VOL_SCALE;
/* Main processing loop */
for (i = 0; i < dev->frames; i++) {
/* Processing per channel */
for (channel = 0; channel < dev->params.channels; channel++) {
/* Load the input sample */
AE_L16_XP(in_sample, in, sizeof(ae_int16));
/* Get gain coefficients */
volume = *((ae_f32 *)&vol_scaled[channel]);
/* Multiply the input sample */
mult = AE_MULFP32X16X2RS_L(volume, in_sample);
/* Shift right and round to get 16 in 32 bits */
out_sample = AE_SRAA32RS(mult, 16);
/* Shift left to get the right alignment */
out_sample = AE_SLAA32(out_sample, shift_left);
/* Store the output sample */
AE_S32_L_XP(out_sample, out, sizeof(ae_int32));
}
}
}
/**
* \brief HiFi3 enabled volume processing from x bit to 16 bit.
* \param[in,out] dev Volume base component device.
* \param[in,out] sink Destination buffer.
* \param[in,out] source Source buffer.
*/
static void vol_sX_to_s16(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source)
{
struct comp_data *cd = comp_get_drvdata(dev);
uint32_t vol_scaled[SOF_IPC_MAX_CHANNELS];
ae_f32x2 volume;
ae_f32x2 mult;
ae_f32x2 in_sample = AE_ZERO32();
ae_f16x4 out_sample;
size_t channel;
uint8_t shift_left = 0;
int i;
ae_int32 *in = (ae_int32 *)source->r_ptr;
ae_int16 *out = (ae_int16 *)sink->w_ptr;
/* Get value of shift left */
if (cd->source_format == SOF_IPC_FRAME_S24_4LE)
shift_left = 8;
/* Scale to VOL_MAX */
for (channel = 0; channel < dev->params.channels; channel++)
vol_scaled[channel] = cd->volume[channel] * VOL_SCALE;
/* Main processing loop */
for (i = 0; i < dev->frames; i++) {
/* Processing per channel */
for (channel = 0; channel < dev->params.channels; channel++) {
/* Load the input sample */
AE_L32_XP(in_sample, in, sizeof(ae_int32));
/* Shift left to get the right alignment */
in_sample = AE_SLAA32(in_sample, shift_left);
/* Get gain coefficients */
volume = *((ae_f32 *)&vol_scaled[channel]);
/* Multiply the input sample */
mult = AE_MULFP32X2RS(volume, in_sample);
/* Shift right to get 16 in 32 bits */
out_sample = AE_MOVF16X4_FROMF32X2
(AE_SRLA32(mult, 16));
/* Store the output sample */
AE_S16_0_XP(out_sample, out, sizeof(ae_int16));
}
}
}
/**
* \brief HiFi3 enabled volume processing from 24/32 bit to 24/32 or 32 bit.
* \param[in,out] dev Volume base component device.
* \param[in,out] sink Destination buffer.
* \param[in,out] source Source buffer.
*/
static void vol_s24_to_s24_s32(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source)
{
struct comp_data *cd = comp_get_drvdata(dev);
uint32_t vol_scaled[SOF_IPC_MAX_CHANNELS];
ae_f32x2 volume;
ae_f32x2 in_sample = AE_ZERO32();
ae_f32x2 out_sample;
ae_f32x2 mult;
size_t channel;
uint8_t shift_left = 0;
int i;
ae_int32 *in = (ae_int32 *)source->r_ptr;
ae_int32 *out = (ae_int32 *)sink->w_ptr;
/* Get value of shift left */
if (cd->sink_format == SOF_IPC_FRAME_S32_LE)
shift_left = 8;
/* Scale to VOL_MAX */
for (channel = 0; channel < dev->params.channels; channel++)
vol_scaled[channel] = cd->volume[channel] * VOL_SCALE;
/* Main processing loop */
for (i = 0; i < dev->frames; i++) {
/* Processing per channel */
for (channel = 0; channel < dev->params.channels; channel++) {
/* Load the input sample */
AE_L32_XP(in_sample, in, sizeof(ae_int32));
/* Get gain coefficients */
volume = *((ae_f32 *)&vol_scaled[channel]);
/* Multiply the input sample */
mult = AE_MULFP32X2RS(volume, AE_SLAA32(in_sample, 8));
/* Shift right to get 24 in 32 bits (LSB) */
out_sample = AE_SRLA32(mult, 8);
/* Shift left to get the right alignment */
out_sample = AE_SLAA32(out_sample, shift_left);
/* Store the output sample */
AE_S32_L_XP(out_sample, out, sizeof(ae_int32));
}
}
}
/**
* \brief HiFi3 enabled volume processing from 32 bit to 24/32 or 32 bit.
* \param[in,out] dev Volume base component device.
* \param[in,out] sink Destination buffer.
* \param[in,out] source Source buffer.
*/
static void vol_s32_to_s24_s32(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source)
{
struct comp_data *cd = comp_get_drvdata(dev);
uint32_t vol_scaled[SOF_IPC_MAX_CHANNELS];
ae_f32x2 volume;
ae_f32x2 in_sample = AE_ZERO32();
ae_f32x2 out_sample;
ae_f32x2 mult;
size_t channel;
uint8_t shift_right = 0;
int i;
ae_int32 *in = (ae_int32 *)source->r_ptr;
ae_int32 *out = (ae_int32 *)sink->w_ptr;
/* Get value of shift right */
if (cd->sink_format == SOF_IPC_FRAME_S24_4LE)
shift_right = 8;
/* Scale to VOL_MAX */
for (channel = 0; channel < dev->params.channels; channel++)
vol_scaled[channel] = cd->volume[channel] * VOL_SCALE;
/* Main processing loop */
for (i = 0; i < dev->frames; i++) {
/* Processing per channel */
for (channel = 0; channel < dev->params.channels; channel++) {
/* Load the input sample */
AE_L32_XP(in_sample, in, sizeof(ae_int32));
/* Get gain coefficients */
volume = *((ae_f32 *)&vol_scaled[channel]);
/* Multiply the input sample */
mult = AE_MULFP32X2RS(volume, in_sample);
/* Shift right to get the right alignment */
out_sample = AE_SRLA32(mult, shift_right);
/* Store the output sample */
AE_S32_L_XP(out_sample, out, sizeof(ae_int32));
}
}
}
const struct comp_func_map func_map[] = {
{SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, 0, vol_s16_to_s16},
{SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, 0, vol_s16_to_sX},
{SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, 0, vol_s16_to_sX},
{SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, 0, vol_sX_to_s16},
{SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, 0, vol_s24_to_s24_s32},
{SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, 0, vol_s24_to_s24_s32},
{SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, 0, vol_sX_to_s16},
{SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, 0, vol_s32_to_s24_s32},
{SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, 0, vol_s32_to_s24_s32},
};
scale_vol vol_get_processing_function(struct comp_dev *dev)
{
struct comp_data *cd = comp_get_drvdata(dev);
int i;
/* map the volume function for source and sink buffers */
for (i = 0; i < ARRAY_SIZE(func_map); i++) {
if (cd->source_format != func_map[i].source)
continue;
if (cd->sink_format != func_map[i].sink)
continue;
return func_map[i].func;
}
return NULL;
}
#endif