Skip to content

Commit 140116e

Browse files
committed
Audio: Selector: Add support for multiple up/down-mix profiles
This patch extends the struct ipc4_selector_coeffs_config. The first 16-bit reserved field is split into to 8-bit field for source and sink channels count. The configuration is changed to array of the previous structs instead of single. When preparing for stream the array is checked for matching number of channels for source and sink and if found the coefficients for the channels counts is selected into use. The change avoids the need for user space to update the configuration in runtime if the blob used for initialization contains all the needed channels up/down-mix profiles. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 4fd12da commit 140116e

2 files changed

Lines changed: 194 additions & 17 deletions

File tree

src/audio/selector/selector.c

Lines changed: 179 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
#include <stddef.h>
3838
#include <stdint.h>
3939

40+
#if CONFIG_IPC_MAJOR_4
41+
#define SEL_MAX_CONFIG_BLOB_SIZE (SEL_MAX_NUM_CONFIGS * sizeof(struct ipc4_selector_coeffs_config))
42+
#endif
43+
4044
LOG_MODULE_REGISTER(selector, CONFIG_SOF_LOG_LEVEL);
4145

4246
#if CONFIG_IPC_MAJOR_3
@@ -574,7 +578,7 @@ static void build_config(struct comp_data *cd, struct module_config *cfg)
574578
/* Build default coefficient array (unity Q10 on diagonal, i.e. pass-through mode) */
575579
memset(&cd->coeffs_config, 0, sizeof(cd->coeffs_config));
576580
for (i = 0; i < MIN(SEL_SOURCE_CHANNELS_MAX, SEL_SINK_CHANNELS_MAX); i++)
577-
cd->coeffs_config.coeffs[i][i] = 1 << 10;
581+
cd->coeffs_config.coeffs[i][i] = SEL_COEF_ONE_Q10;
578582
}
579583

580584
static int selector_init(struct processing_module *mod)
@@ -733,6 +737,7 @@ static int selector_free(struct processing_module *mod)
733737

734738
comp_dbg(mod->dev, "entry");
735739

740+
mod_free(mod, cd->multi_coeffs_config);
736741
mod_free(mod, cd);
737742

738743
return 0;
@@ -769,13 +774,51 @@ static int selector_set_config(struct processing_module *mod, uint32_t config_id
769774
size_t response_size)
770775
{
771776
struct comp_data *cd = module_get_private_data(mod);
777+
struct comp_dev *dev = mod->dev;
778+
int n;
772779

773780
if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID) {
774-
if (data_offset_size != sizeof(cd->coeffs_config))
781+
if (data_offset_size > SEL_MAX_CONFIG_BLOB_SIZE ||
782+
fragment_size < data_offset_size ||
783+
pos != MODULE_CFG_FRAGMENT_SINGLE) {
784+
comp_err(dev, "Failure with data_offset_size %u fragment_size %u pos %u",
785+
data_offset_size, (uint32_t)fragment_size, (uint32_t)pos);
775786
return -EINVAL;
787+
}
776788

777-
memcpy_s(&cd->coeffs_config, sizeof(cd->coeffs_config), fragment, data_offset_size);
778-
return 0;
789+
/* The size must be N times the coefficient vectors size of one channels
790+
* up/down mix profile.
791+
*/
792+
n = data_offset_size / sizeof(struct ipc4_selector_coeffs_config);
793+
if (n < 1 || data_offset_size != n * sizeof(struct ipc4_selector_coeffs_config)) {
794+
comp_err(dev, "Invalid configuration size.");
795+
return -EINVAL;
796+
}
797+
798+
cd->num_configs = n;
799+
if (cd->multi_coeffs_config && cd->multi_coeffs_config_size < data_offset_size) {
800+
/* Configuration exist but the allocation is too small to write over. */
801+
mod_free(mod, cd->multi_coeffs_config);
802+
cd->multi_coeffs_config = NULL;
803+
}
804+
805+
if (!cd->multi_coeffs_config) {
806+
cd->multi_coeffs_config_size = data_offset_size;
807+
cd->multi_coeffs_config = mod_alloc(mod, cd->multi_coeffs_config_size);
808+
if (!cd->multi_coeffs_config) {
809+
comp_err(dev, "Failed to allocate configuration blob.");
810+
return -ENOMEM;
811+
}
812+
}
813+
814+
/* Copy the configuration and notify for need to re-configure. If for
815+
* some reason the memcpy_s() would return an error (it can't because
816+
* of the previous checks), the partial or incorrect blob would remain
817+
* allocated but be freed later in module error handling and ending.
818+
*/
819+
cd->new_config = true;
820+
return memcpy_s(cd->multi_coeffs_config, cd->multi_coeffs_config_size,
821+
fragment, data_offset_size);
779822
}
780823

781824
return -EINVAL;
@@ -788,6 +831,112 @@ static int selector_get_config(struct processing_module *mod, uint32_t config_id
788831
return 0;
789832
}
790833

834+
/**
835+
* \brief Loop the array of mix coefficients sets and find a set with matching channels
836+
* in and out count.
837+
* \param[in] cd Selector component data.
838+
* \param[in] source_channels Number of channels in source.
839+
* \param[in] sink_channels Number of channels in sink.
840+
*
841+
* \return Pointer to the matching ipc4_selector_coeffs_config if found, or NULL if
842+
* no matching configuration exists.
843+
*/
844+
static struct ipc4_selector_coeffs_config *selector_config_array_search(struct comp_data *cd,
845+
int source_channels,
846+
int sink_channels)
847+
{
848+
struct ipc4_selector_coeffs_config *found = NULL;
849+
int i;
850+
851+
for (i = 0; i < cd->num_configs; i++) {
852+
if (cd->multi_coeffs_config[i].source_channels_count == source_channels &&
853+
cd->multi_coeffs_config[i].sink_channels_count == sink_channels) {
854+
found = &cd->multi_coeffs_config[i];
855+
break;
856+
}
857+
}
858+
859+
return found;
860+
}
861+
862+
/**
863+
* \brief Get mix coefficients set from configuration blob with multiple coefficients sets.
864+
* Also activate more efficient pass-through copy mode if the coefficients indicate 1:1
865+
* copy from source to sink.
866+
* \param[in,out] mod Selector base module device.
867+
* \param[in] source_channels Number of channels in source.
868+
* \param[in] sink_channels Number of channels in sink.
869+
*
870+
* \return Error code.
871+
*/
872+
static int selector_find_coefficients(struct processing_module *mod)
873+
{
874+
struct comp_data *cd = module_get_private_data(mod);
875+
struct comp_dev *dev = mod->dev;
876+
struct ipc4_selector_coeffs_config *config;
877+
uint32_t source_channels = cd->config.in_channels_count;
878+
uint32_t sink_channels = cd->config.out_channels_count;
879+
int16_t coef;
880+
int ret, i, j;
881+
882+
/* In set_config() the blob is copied to cd->multi_coeffs_config. A legacy blob contains a
883+
* single set of mix coefficients without channels information. A new blob with multiple
884+
* configurations has the source and sink channels count information. If there has been no
885+
* set_config(), then the cd->coeffs_config has been initialized in set_selector_params()
886+
* to mix with coefficients SEL_COEF_ONE_Q10 for matching input and output channels.
887+
*/
888+
if (cd->multi_coeffs_config) {
889+
if (cd->num_configs > 1) {
890+
config = selector_config_array_search(cd, source_channels, sink_channels);
891+
/* If not found, check if pass-through mix is defined for the max
892+
* channels count (8).
893+
*/
894+
if (!config && source_channels == sink_channels)
895+
config = selector_config_array_search(cd, SEL_SOURCE_CHANNELS_MAX,
896+
SEL_SINK_CHANNELS_MAX);
897+
898+
if (!config) {
899+
comp_err(dev, "No mix coefficients found for %u to %u channels.",
900+
source_channels, sink_channels);
901+
return -EINVAL;
902+
}
903+
} else {
904+
config = cd->multi_coeffs_config;
905+
}
906+
907+
ret = memcpy_s(&cd->coeffs_config, sizeof(struct ipc4_selector_coeffs_config),
908+
config, sizeof(*config));
909+
if (ret)
910+
return ret;
911+
}
912+
913+
/* The pass-through copy function can be used if coefficients are a unit matrix for
914+
* 1:1 stream copy.
915+
*/
916+
if (source_channels == sink_channels) {
917+
cd->passthrough = true;
918+
for (i = 0; i < sink_channels; i++) {
919+
for (j = 0; j < source_channels; j++) {
920+
coef = cd->coeffs_config.coeffs[i][j];
921+
if ((i == j && coef != SEL_COEF_ONE_Q10) || (i != j && coef != 0)) {
922+
cd->passthrough = false;
923+
break;
924+
}
925+
}
926+
}
927+
} else {
928+
cd->passthrough = false;
929+
}
930+
931+
if (cd->passthrough)
932+
comp_info(dev, "Passthrough mode.");
933+
else
934+
comp_info(dev, "Using coefficients for %u to %u channels.",
935+
source_channels, sink_channels);
936+
937+
return 0;
938+
}
939+
791940
/**
792941
* \brief Copies and processes stream data.
793942
* \param[in,out] mod Selector base module device.
@@ -799,11 +948,29 @@ static int selector_process(struct processing_module *mod,
799948
struct output_stream_buffer *output_buffers,
800949
int num_output_buffers)
801950
{
951+
struct audio_stream *source;
952+
struct audio_stream *sink;
802953
struct comp_data *cd = module_get_private_data(mod);
803954
uint32_t avail_frames = input_buffers[0].size;
955+
int ret;
804956

805957
comp_dbg(mod->dev, "entry");
806958

959+
if (cd->new_config) {
960+
cd->new_config = false;
961+
ret = selector_find_coefficients(mod);
962+
if (ret)
963+
return ret;
964+
}
965+
966+
if (cd->passthrough) {
967+
source = input_buffers->data;
968+
sink = output_buffers->data;
969+
audio_stream_copy(source, 0, sink, 0, avail_frames * cd->config.in_channels_count);
970+
module_update_buffer_position(input_buffers, output_buffers, avail_frames);
971+
return 0;
972+
}
973+
807974
if (avail_frames)
808975
/* copy selected channels from in to out */
809976
cd->sel_func(mod, input_buffers, output_buffers, avail_frames);
@@ -850,17 +1017,7 @@ static int selector_prepare(struct processing_module *mod,
8501017
/* get sink data format and period bytes */
8511018
cd->sink_format = audio_stream_get_frm_fmt(&sinkb->stream);
8521019
cd->sink_period_bytes = audio_stream_period_bytes(&sinkb->stream, dev->frames);
853-
854-
/* There is an assumption that sink component will report out
855-
* proper number of channels [1] for selector to actually
856-
* reduce channel count between source and sink
857-
*/
858-
comp_info(dev, "source sink channel = %u %u",
859-
audio_stream_get_channels(&sourceb->stream),
860-
audio_stream_get_channels(&sinkb->stream));
861-
8621020
sink_size = audio_stream_get_size(&sinkb->stream);
863-
8641021
md->mpd.in_buff_size = cd->source_period_bytes;
8651022
md->mpd.out_buff_size = cd->sink_period_bytes;
8661023

@@ -891,6 +1048,11 @@ static int selector_prepare(struct processing_module *mod,
8911048
return -EINVAL;
8921049
}
8931050

1051+
if (cd->new_config) {
1052+
cd->new_config = false;
1053+
return selector_find_coefficients(mod);
1054+
}
1055+
8941056
return 0;
8951057
}
8961058

@@ -908,7 +1070,9 @@ static int selector_reset(struct processing_module *mod)
9081070
cd->source_period_bytes = 0;
9091071
cd->sink_period_bytes = 0;
9101072
cd->sel_func = NULL;
911-
1073+
cd->num_configs = 0;
1074+
cd->passthrough = false;
1075+
cd->new_config = false;
9121076
return 0;
9131077
}
9141078

src/include/sof/audio/selector.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
struct comp_buffer;
2828
struct comp_dev;
2929

30+
/** \brief Default mix gain. */
31+
#define SEL_COEF_ONE_Q10 1024 /* int16(1 * 2^10) */
32+
3033
#if CONFIG_IPC_MAJOR_3
3134
/** \brief Supported channel count on input. */
3235
#define SEL_SOURCE_2CH 2
@@ -43,6 +46,9 @@ struct comp_dev;
4346
/** \brief Maximum supported channel count on output. */
4447
#define SEL_SINK_CHANNELS_MAX 8
4548

49+
/** \brief Maximum number of configurations in the blob received with set_config() */
50+
#define SEL_MAX_NUM_CONFIGS 8
51+
4652
#define SEL_NUM_IN_PIN_FMTS 1
4753
#define SEL_NUM_OUT_PIN_FMTS 1
4854

@@ -60,8 +66,10 @@ enum ipc4_selector_config_id {
6066

6167
/** \brief IPC4 mixing coefficients configuration. */
6268
struct ipc4_selector_coeffs_config {
63-
uint16_t rsvd0; /**< Unused field, keeps the structure aligned with common layout */
64-
uint16_t rsvd1; /**< Unused field, keeps the structure aligned with common layout */
69+
uint8_t source_channels_count; /**< Used when multiple profiles are packed into one blob. */
70+
uint8_t sink_channels_count; /**< Used when multiple profiles are packed into one blob. */
71+
uint8_t source_channel_config; /**< Used when multiple profiles are packed into one blob. */
72+
uint8_t sink_channel_config; /**< Used when multiple profiles are packed into one blob. */
6573

6674
/** Mixing coefficients in Q10 fixed point format */
6775
int16_t coeffs[SEL_SINK_CHANNELS_MAX][SEL_SOURCE_CHANNELS_MAX];
@@ -109,6 +117,8 @@ struct comp_data {
109117
#if CONFIG_IPC_MAJOR_4
110118
struct sof_selector_ipc4_config sel_ipc4_cfg;
111119
struct ipc4_selector_coeffs_config coeffs_config;
120+
struct ipc4_selector_coeffs_config *multi_coeffs_config;
121+
size_t multi_coeffs_config_size;
112122
#endif
113123

114124
uint32_t source_period_bytes; /**< source number of period bytes */
@@ -117,6 +127,9 @@ struct comp_data {
117127
enum sof_ipc_frame sink_format; /**< sink frame format */
118128
struct sof_sel_config config; /**< component configuration data */
119129
sel_func sel_func; /**< channel selector processing function */
130+
int num_configs; /**< Number of coefficients sets in configuration blob. */
131+
bool passthrough; /**< Use a passthrough copy function when no up/down mix. */
132+
bool new_config; /**< True if new configuration has been received */
120133
};
121134

122135
/** \brief Selector processing functions map. */

0 commit comments

Comments
 (0)