Skip to content

Commit d80ed44

Browse files
singalsulgirdwood
authored andcommitted
EQ IIR: Add support for response reconfigure during streaming
This patch adds capability to equalizer to switch response during audio playback or capture. The glitches are minimized by check and apply of the filter configuration before each copy() operation. The existing only halfway implemented SOF_CTRL_CMD_ENUM for quick stored presets switching during streaming is removed for now since there is no driver support. The internal code cleanup includes extract of two functions from eq_iir_setup() for check and initialize of coefficients and initialize of filter delay lines. It helps to better understand the setup flow. The extrapolation of channels to response assign map is changed to use previous channel response instead of first if the stream contains more channels than the response definition. The procedure was changed to avoid a code static analysis issue. The behavior is not specified so this simpler way was chosen. To minimize number of duplicated functions in generic and HiFi3 version a new module iir_generic.c is created for the filter core. After the change iir.c contains only the common code for both implementations. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 1d92e8e commit d80ed44

File tree

6 files changed

+244
-266
lines changed

6 files changed

+244
-266
lines changed

src/audio/eq_iir/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# SPDX-License-Identifier: BSD-3-Clause
22

3-
add_local_sources(sof eq_iir.c iir.c iir_hifi3.c)
3+
add_local_sources(sof eq_iir.c iir.c iir_generic.c iir_hifi3.c)

src/audio/eq_iir/eq_iir.c

Lines changed: 123 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@
5050
/* IIR component private data */
5151
struct comp_data {
5252
struct iir_state_df2t iir[PLATFORM_MAX_CHANNELS]; /**< filters state */
53-
struct sof_eq_iir_config *config; /**< pointer to setup blob */
54-
enum sof_ipc_frame source_format; /**< source frame format */
55-
enum sof_ipc_frame sink_format; /**< sink frame format */
56-
int64_t *iir_delay; /**< pointer to allocated RAM */
57-
size_t iir_delay_size; /**< allocated size */
53+
struct sof_eq_iir_config *config; /**< pointer to setup blob */
54+
struct sof_eq_iir_config *config_new; /**< pointer to new setup */
55+
enum sof_ipc_frame source_format; /**< source frame format */
56+
enum sof_ipc_frame sink_format; /**< sink frame format */
57+
int64_t *iir_delay; /**< pointer to allocated RAM */
58+
size_t iir_delay_size; /**< allocated size */
5859
void (*eq_iir_func)(struct comp_dev *dev,
5960
struct comp_buffer *source,
6061
struct comp_buffer *sink,
@@ -383,38 +384,32 @@ static void eq_iir_free_delaylines(struct comp_data *cd)
383384
iir[i].delay = NULL;
384385
}
385386

386-
static int eq_iir_setup(struct comp_data *cd, int nch)
387+
static int eq_iir_init_coef(struct sof_eq_iir_config *config,
388+
struct iir_state_df2t *iir, int nch)
387389
{
388-
struct iir_state_df2t *iir = cd->iir;
389-
struct sof_eq_iir_config *config = cd->config;
390390
struct sof_eq_iir_header_df2t *lookup[SOF_EQ_IIR_MAX_RESPONSES];
391391
struct sof_eq_iir_header_df2t *eq;
392-
int64_t *iir_delay;
393-
int32_t *coef_data, *assign_response;
394-
size_t s;
395-
size_t size_sum = 0;
392+
int32_t *assign_response;
393+
int32_t *coef_data;
394+
int size_sum = 0;
395+
int resp = 0;
396396
int i;
397397
int j;
398-
int resp;
399-
400-
/* Free existing IIR channels data if it was allocated */
401-
eq_iir_free_delaylines(cd);
398+
int s;
402399

403-
trace_eq("eq_iir_setup(), "
404-
"channels_in_config = %u, number_of_responses = %u",
405-
config->channels_in_config, config->number_of_responses);
400+
trace_eq("eq_iir_init_coef(), response assign for %u channels, "
401+
"%u responses", config->channels_in_config,
402+
config->number_of_responses);
406403

407404
/* Sanity checks */
408405
if (nch > PLATFORM_MAX_CHANNELS ||
409406
config->channels_in_config > PLATFORM_MAX_CHANNELS ||
410407
!config->channels_in_config) {
411-
trace_eq_error("eq_iir_setup() error: "
412-
"invalid nch or channels_in_config");
408+
trace_eq_error("eq_iir_init_coef(), invalid channels count");
413409
return -EINVAL;
414410
}
415411
if (config->number_of_responses > SOF_EQ_IIR_MAX_RESPONSES) {
416-
trace_eq_error("eq_iir_setup() error: number_of_responses"
417-
" > SOF_EQ_IIR_MAX_RESPONSES");
412+
trace_eq_error("eq_iir_init_coef(), # of resp exceeds max");
418413
return -EINVAL;
419414
}
420415

@@ -438,15 +433,13 @@ static int eq_iir_setup(struct comp_data *cd, int nch)
438433
/* Initialize 1st phase */
439434
for (i = 0; i < nch; i++) {
440435
/* Check for not reading past blob response to channel assign
441-
* map. If the blob has smaller channel map then apply for
442-
* additional channels the response that was used for the first
443-
* channel. This allows to use mono blobs to setup multi
444-
* channel equalization without stopping to an error.
436+
* map. The previous channel response is assigned for any
437+
* additional channels in the stream. It allows to use single
438+
* channel configuration to setup multi channel equalization
439+
* with the same response.
445440
*/
446441
if (i < config->channels_in_config)
447442
resp = assign_response[i];
448-
else
449-
resp = assign_response[0];
450443

451444
if (resp < 0) {
452445
/* Initialize EQ channel to bypass and continue with
@@ -461,52 +454,66 @@ static int eq_iir_setup(struct comp_data *cd, int nch)
461454

462455
/* Initialize EQ coefficients */
463456
eq = lookup[resp];
464-
s = iir_init_coef_df2t(&iir[i], eq);
465-
if (s > 0)
457+
s = iir_delay_size_df2t(eq);
458+
if (s > 0) {
466459
size_sum += s;
467-
else
460+
} else {
468461
return -EINVAL;
462+
}
469463

470-
trace_eq("eq_iir_setup(), "
471-
"ch = %d initialized to response = %d", i, resp);
464+
iir_init_coef_df2t(&iir[i], eq);
465+
trace_eq("eq_iir_init_coef(), ch %d is set to response %d",
466+
i, resp);
472467
}
473468

474-
/* If all channels were set to bypass there's no need to
475-
* allocate delay. Just return with success.
476-
*/
477-
cd->iir_delay = NULL;
478-
cd->iir_delay_size = size_sum;
479-
if (!size_sum)
480-
return 0;
481-
/* Allocate all IIR channels data in a big chunk and clear it */
482-
cd->iir_delay = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, size_sum);
483-
if (!cd->iir_delay)
484-
return -ENOMEM;
469+
return size_sum;
470+
}
485471

486-
memset(cd->iir_delay, 0, size_sum);
472+
static void eq_iir_init_delay(struct iir_state_df2t *iir,
473+
int64_t *delay_start, int nch)
474+
{
475+
int64_t *delay = delay_start;
476+
int i;
487477

488-
/* Initialize 2nd phase to set EQ delay lines pointers */
489-
iir_delay = cd->iir_delay;
478+
/* Initialize second phase to set EQ delay lines pointers. A
479+
* bypass mode filter is indicated by biquads count of zero.
480+
*/
490481
for (i = 0; i < nch; i++) {
491-
resp = assign_response[i];
492-
if (resp >= 0)
493-
iir_init_delay_df2t(&iir[i], &iir_delay);
482+
if (iir[i].biquads > 0)
483+
iir_init_delay_df2t(&iir[i], &delay);
494484
}
495-
return 0;
496485
}
497486

498-
static int eq_iir_switch_store(struct iir_state_df2t iir[],
499-
struct sof_eq_iir_config *config,
500-
uint32_t ch, int32_t response)
487+
static int eq_iir_setup(struct comp_data *cd, int nch)
501488
{
502-
/* Copy assign response from update. The EQ is initialized later
503-
* when all channels have been updated.
489+
int delay_size;
490+
491+
/* Free existing IIR channels data if it was allocated */
492+
eq_iir_free_delaylines(cd);
493+
494+
/* Set coefficients for each channel EQ from coefficient blob */
495+
delay_size = eq_iir_init_coef(cd->config, cd->iir, nch);
496+
if (delay_size < 0)
497+
return delay_size; /* Contains error code */
498+
499+
/* If all channels were set to bypass there's no need to
500+
* allocate delay. Just return with success.
504501
*/
505-
if (!config || ch >= config->channels_in_config)
506-
return -EINVAL;
502+
if (!delay_size)
503+
return 0;
504+
505+
/* Allocate all IIR channels data in a big chunk and clear it */
506+
cd->iir_delay = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, delay_size);
507+
if (!cd->iir_delay) {
508+
trace_eq_error("eq_iir_setup(), delay allocation fail");
509+
return -ENOMEM;
510+
}
507511

508-
config->data[ch] = response;
512+
memset(cd->iir_delay, 0, delay_size);
513+
cd->iir_delay_size = delay_size;
509514

515+
/* Assign delay line to each channel EQ */
516+
eq_iir_init_delay(cd->iir, cd->iir_delay, nch);
510517
return 0;
511518
}
512519

@@ -563,6 +570,7 @@ static struct comp_dev *eq_iir_new(struct sof_ipc_comp *comp)
563570
cd->iir_delay = NULL;
564571
cd->iir_delay_size = 0;
565572
cd->config = NULL;
573+
cd->config_new = NULL;
566574

567575
/* Allocate and make a copy of the coefficients blob and reset IIR. If
568576
* the EQ is configured later in run-time the size is zero.
@@ -594,6 +602,7 @@ static void eq_iir_free(struct comp_dev *dev)
594602

595603
eq_iir_free_delaylines(cd);
596604
eq_iir_free_parameters(&cd->config);
605+
eq_iir_free_parameters(&cd->config_new);
597606

598607
rfree(cd);
599608
rfree(dev);
@@ -654,86 +663,58 @@ static int iir_cmd_set_data(struct comp_dev *dev,
654663
struct sof_ipc_ctrl_data *cdata)
655664
{
656665
struct comp_data *cd = comp_get_drvdata(dev);
657-
struct sof_ipc_ctrl_value_comp *compv;
658-
struct sof_eq_iir_config *cfg;
666+
struct sof_eq_iir_config *request;
659667
size_t bs;
660-
int i;
661668
int ret = 0;
662669

663670
switch (cdata->cmd) {
664-
case SOF_CTRL_CMD_ENUM:
665-
trace_eq_with_ids(dev, "iir_cmd_set_data(), SOF_CTRL_CMD_ENUM");
666-
compv = (struct sof_ipc_ctrl_value_comp *)cdata->data->data;
667-
if (cdata->index == SOF_EQ_IIR_IDX_SWITCH) {
668-
for (i = 0; i < (int)cdata->num_elems; i++) {
669-
trace_eq_with_ids(dev, "iir_cmd_set_data(),"
670-
"SOF_EQ_IIR_IDX_SWITCH, "
671-
"compv index = %u, "
672-
"svalue = %u",
673-
compv[i].index,
674-
compv[i].svalue);
675-
ret = eq_iir_switch_store(cd->iir,
676-
cd->config,
677-
compv[i].index,
678-
compv[i].svalue);
679-
if (ret < 0) {
680-
trace_eq_error_with_ids(dev,
681-
"iir_cmd_set_data() "
682-
"error:"
683-
"eq_iir_switch_store()"
684-
" failed");
685-
return -EINVAL;
686-
}
687-
}
688-
} else {
689-
trace_eq_error_with_ids(dev, "iir_cmd_set_data() error:"
690-
"invalid cdata->index = %u",
691-
cdata->index);
692-
return -EINVAL;
693-
}
694-
break;
695671
case SOF_CTRL_CMD_BINARY:
696672
trace_eq_with_ids(dev, "iir_cmd_set_data(), SOF_CTRL_CMD_BINARY");
697673

698-
if (dev->state != COMP_STATE_READY) {
699-
/* It is a valid request but currently this is not
700-
* supported during playback/capture. The driver will
701-
* re-send data in next resume when idle and the new
702-
* EQ configuration will be used when playback/capture
703-
* starts.
704-
*/
705-
trace_eq_error_with_ids(dev, "iir_cmd_set_data() error: "
706-
"driver is busy");
707-
return -EBUSY;
708-
}
709-
710-
/* Check and free old config */
711-
eq_iir_free_parameters(&cd->config);
712-
713-
/* Copy new config, find size from header */
714-
cfg = (struct sof_eq_iir_config *)cdata->data->data;
715-
bs = cfg->size;
716-
trace_eq_with_ids(dev, "iir_cmd_set_data(), blob size = %u",
717-
bs);
674+
/* Find size from header */
675+
request = (struct sof_eq_iir_config *)cdata->data->data;
676+
bs = request->size;
718677
if (bs > SOF_EQ_IIR_MAX_SIZE || bs == 0) {
719678
trace_eq_error_with_ids(dev, "iir_cmd_set_data() error: "
720679
"invalid blob size");
721680
return -EINVAL;
722681
}
723682

683+
/* Check that there is no work-in-progress previous request */
684+
if (cd->config_new) {
685+
trace_eq_error_with_ids(dev, "iir_cmd_set_data(), busy with previous");
686+
return -EBUSY;
687+
}
688+
724689
/* Allocate and make a copy of the blob and setup IIR */
725-
cd->config = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, bs);
726-
if (!cd->config) {
727-
trace_eq_error_with_ids(dev, "iir_cmd_set_data() error: "
728-
"alloc failed");
690+
cd->config_new = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, bs);
691+
if (!cd->config_new) {
692+
trace_eq_error_with_ids(dev, "iir_cmd_set_data(), alloc fail");
729693
return -EINVAL;
730694
}
731695

732-
/* Just copy the configurate. The EQ will be initialized in
733-
* prepare().
696+
/* Copy the configuration. If the component state is ready
697+
* the EQ will initialize in prepare().
734698
*/
735-
ret = memcpy_s(cd->config, bs, cdata->data->data, bs);
699+
ret = memcpy_s(cd->config_new, bs, cdata->data->data, bs);
736700
assert(!ret);
701+
702+
/* If component state is READY we can omit old configuration
703+
* immediately. When in playback/capture the new configuration
704+
* presence is checked in copy().
705+
*/
706+
if (dev->state == COMP_STATE_READY)
707+
eq_iir_free_parameters(&cd->config);
708+
709+
/* If there is no existing configuration the received can
710+
* be set to current immediately. It will be applied in
711+
* prepare() when streaming starts.
712+
*/
713+
if (!cd->config) {
714+
cd->config = cd->config_new;
715+
cd->config_new = NULL;
716+
}
717+
737718
break;
738719
default:
739720
trace_eq_error_with_ids(dev, "iir_cmd_set_data() error: "
@@ -761,12 +742,6 @@ static int eq_iir_cmd(struct comp_dev *dev, int cmd, void *data,
761742
case COMP_CMD_GET_DATA:
762743
ret = iir_cmd_get_data(dev, cdata, max_data_size);
763744
break;
764-
case COMP_CMD_SET_VALUE:
765-
trace_eq_with_ids(dev, "eq_iir_cmd(), COMP_CMD_SET_VALUE");
766-
break;
767-
case COMP_CMD_GET_VALUE:
768-
trace_eq_with_ids(dev, "eq_iir_cmd(), COMP_CMD_GET_VALUE");
769-
break;
770745
default:
771746
trace_eq_error_with_ids(dev, "eq_iir_cmd() error: "
772747
"invalid command");
@@ -796,6 +771,18 @@ static int eq_iir_copy(struct comp_dev *dev)
796771

797772
tracev_eq_with_ids(dev, "eq_iir_copy()");
798773

774+
/* Check for changed configuration */
775+
if (cd->config_new) {
776+
eq_iir_free_parameters(&cd->config);
777+
cd->config = cd->config_new;
778+
cd->config_new = NULL;
779+
ret = eq_iir_setup(cd, dev->params.channels);
780+
if (ret < 0) {
781+
trace_eq_error_with_ids(dev, "eq_iir_copy(), failed IIR setup");
782+
return ret;
783+
}
784+
}
785+
799786
/* Get source, sink, number of frames etc. to process. */
800787
ret = comp_get_copy_limits(dev, &cl);
801788
if (ret < 0) {
@@ -919,12 +906,18 @@ static int eq_iir_reset(struct comp_dev *dev)
919906
static void eq_iir_cache(struct comp_dev *dev, int cmd)
920907
{
921908
struct comp_data *cd;
909+
struct sof_eq_iir_config *cn;
922910

923911
switch (cmd) {
924912
case CACHE_WRITEBACK_INV:
925913
trace_eq_with_ids(dev, "eq_iir_cache(), CACHE_WRITEBACK_INV");
926914

927915
cd = comp_get_drvdata(dev);
916+
if (cd->config_new) {
917+
cn = cd->config_new;
918+
dcache_writeback_invalidate_region(cn, cn->size);
919+
}
920+
928921
if (cd->config)
929922
dcache_writeback_invalidate_region(cd->config,
930923
cd->config->size);
@@ -955,6 +948,9 @@ static void eq_iir_cache(struct comp_dev *dev, int cmd)
955948
if (cd->config)
956949
dcache_invalidate_region(cd->config,
957950
cd->config->size);
951+
if (cd->config_new)
952+
dcache_invalidate_region(cd->config_new,
953+
cd->config_new->size);
958954
break;
959955
}
960956
}

0 commit comments

Comments
 (0)