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+
4044LOG_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
580584static 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,49 @@ 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+ pos != MODULE_CFG_FRAGMENT_SINGLE ) {
783+ comp_err (dev , "Failure with size %u pos %u" , data_offset_size , pos );
775784 return - EINVAL ;
785+ }
776786
777- memcpy_s (& cd -> coeffs_config , sizeof (cd -> coeffs_config ), fragment , data_offset_size );
778- return 0 ;
787+ /* The size must be N times the coefficient vectors size of one channels
788+ * up/down mix profile.
789+ */
790+ n = data_offset_size / sizeof (struct ipc4_selector_coeffs_config );
791+ if (n < 1 || data_offset_size != n * sizeof (struct ipc4_selector_coeffs_config )) {
792+ comp_err (dev , "Invalid configuration size." );
793+ return - EINVAL ;
794+ }
795+
796+ cd -> num_configs = n ;
797+ if (cd -> multi_coeffs_config && cd -> multi_coeffs_config_size < data_offset_size ) {
798+ /* Configuration exist but the allocation is too small to write over. */
799+ mod_free (mod , cd -> multi_coeffs_config );
800+ cd -> multi_coeffs_config = NULL ;
801+ }
802+
803+ if (!cd -> multi_coeffs_config ) {
804+ cd -> multi_coeffs_config_size = data_offset_size ;
805+ cd -> multi_coeffs_config = mod_alloc (mod , cd -> multi_coeffs_config_size );
806+ if (!cd -> multi_coeffs_config ) {
807+ comp_err (dev , "Failed to allocate configuration blob." );
808+ return - ENOMEM ;
809+ }
810+ }
811+
812+ /* Copy the configuration and notify for need to re-configure. If for
813+ * some reason the memcpy_s() would return an error (it can't because
814+ * of the previous checks), the partial or incorrect blob would remain
815+ * allocated but be freed later in module error handling and ending.
816+ */
817+ cd -> new_config = true;
818+ return memcpy_s (cd -> multi_coeffs_config , cd -> multi_coeffs_config_size ,
819+ fragment , data_offset_size );
779820 }
780821
781822 return - EINVAL ;
@@ -788,6 +829,112 @@ static int selector_get_config(struct processing_module *mod, uint32_t config_id
788829 return 0 ;
789830}
790831
832+ /**
833+ * \brief Loop the array of mix coefficients sets and find a set with matching channels
834+ * in and out count.
835+ * \param[in] cd Selector component data.
836+ * \param[in] source_channels Number of channels in source.
837+ * \param[in] sink_channels Number of channels in sink.
838+ *
839+ * \return Pointer to the matching ipc4_selector_coeffs_config if found, or NULL if
840+ * no matching configuration exists.
841+ */
842+ static struct ipc4_selector_coeffs_config * selector_config_array_search (struct comp_data * cd ,
843+ int source_channels ,
844+ int sink_channels )
845+ {
846+ struct ipc4_selector_coeffs_config * found = NULL ;
847+ int i ;
848+
849+ for (i = 0 ; i < cd -> num_configs ; i ++ ) {
850+ if (cd -> multi_coeffs_config [i ].source_channels_count == source_channels &&
851+ cd -> multi_coeffs_config [i ].sink_channels_count == sink_channels ) {
852+ found = & cd -> multi_coeffs_config [i ];
853+ break ;
854+ }
855+ }
856+
857+ return found ;
858+ }
859+
860+ /**
861+ * \brief Get mix coefficients set from configuration blob with multiple coefficients sets.
862+ * Also activate more efficient pass-through copy mode if the coefficients indicate 1:1
863+ * copy from source to sink.
864+ * \param[in,out] mod Selector base module device.
865+ * \param[in] source_channels Number of channels in source.
866+ * \param[in] sink_channels Number of channels in sink.
867+ *
868+ * \return Error code.
869+ */
870+ static int selector_find_coefficients (struct processing_module * mod )
871+ {
872+ struct comp_data * cd = module_get_private_data (mod );
873+ struct comp_dev * dev = mod -> dev ;
874+ struct ipc4_selector_coeffs_config * config ;
875+ uint32_t source_channels = cd -> config .in_channels_count ;
876+ uint32_t sink_channels = cd -> config .out_channels_count ;
877+ int16_t coef ;
878+ int ret , i , j ;
879+
880+ /* In set_config() the blob is copied to cd->multi_coeffs_config. A legacy blob contains a
881+ * single set of mix coefficients without channels information. A new blob with multiple
882+ * configurations has the source and sink channels count information. If there has been no
883+ * set_config(), then the cd->coeffs_config has been initialized in set_selector_params()
884+ * to mix with coefficients SEL_COEF_ONE_Q10 for matching input and output channels.
885+ */
886+ if (cd -> multi_coeffs_config ) {
887+ if (cd -> num_configs > 1 ) {
888+ config = selector_config_array_search (cd , source_channels , sink_channels );
889+ /* If not found, check if pass-through mix is defined for the max
890+ * channels count (8).
891+ */
892+ if (!config && source_channels == sink_channels )
893+ config = selector_config_array_search (cd , SEL_SOURCE_CHANNELS_MAX ,
894+ SEL_SINK_CHANNELS_MAX );
895+
896+ if (!config ) {
897+ comp_err (dev , "No mix coefficients found for %d to %d channels." ,
898+ source_channels , sink_channels );
899+ return - EINVAL ;
900+ }
901+ } else {
902+ config = cd -> multi_coeffs_config ;
903+ }
904+
905+ ret = memcpy_s (& cd -> coeffs_config , sizeof (struct ipc4_selector_coeffs_config ),
906+ config , sizeof (* config ));
907+ if (ret )
908+ return ret ;
909+ }
910+
911+ /* The pass-through copy function can be used if coefficients are a unit matrix for
912+ * 1:1 stream copy.
913+ */
914+ if (source_channels == sink_channels ) {
915+ cd -> passthrough = true;
916+ for (i = 0 ; i < sink_channels ; i ++ ) {
917+ for (j = 0 ; j < source_channels ; j ++ ) {
918+ coef = cd -> coeffs_config .coeffs [i ][j ];
919+ if ((i == j && coef != SEL_COEF_ONE_Q10 ) || (i != j && coef != 0 )) {
920+ cd -> passthrough = false;
921+ break ;
922+ }
923+ }
924+ }
925+ } else {
926+ cd -> passthrough = false;
927+ }
928+
929+ if (cd -> passthrough )
930+ comp_info (dev , "Passthrough mode." );
931+ else
932+ comp_info (dev , "Using coefficients for %d to %d channels." ,
933+ source_channels , sink_channels );
934+
935+ return 0 ;
936+ }
937+
791938/**
792939 * \brief Copies and processes stream data.
793940 * \param[in,out] mod Selector base module device.
@@ -799,11 +946,29 @@ static int selector_process(struct processing_module *mod,
799946 struct output_stream_buffer * output_buffers ,
800947 int num_output_buffers )
801948{
949+ struct audio_stream * source ;
950+ struct audio_stream * sink ;
802951 struct comp_data * cd = module_get_private_data (mod );
803952 uint32_t avail_frames = input_buffers [0 ].size ;
953+ int ret ;
804954
805955 comp_dbg (mod -> dev , "entry" );
806956
957+ if (cd -> new_config ) {
958+ cd -> new_config = false;
959+ ret = selector_find_coefficients (mod );
960+ if (ret )
961+ return ret ;
962+ }
963+
964+ if (cd -> passthrough ) {
965+ source = input_buffers -> data ;
966+ sink = output_buffers -> data ;
967+ audio_stream_copy (source , 0 , sink , 0 , avail_frames * cd -> config .in_channels_count );
968+ module_update_buffer_position (input_buffers , output_buffers , avail_frames );
969+ return 0 ;
970+ }
971+
807972 if (avail_frames )
808973 /* copy selected channels from in to out */
809974 cd -> sel_func (mod , input_buffers , output_buffers , avail_frames );
@@ -850,17 +1015,7 @@ static int selector_prepare(struct processing_module *mod,
8501015 /* get sink data format and period bytes */
8511016 cd -> sink_format = audio_stream_get_frm_fmt (& sinkb -> stream );
8521017 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-
8621018 sink_size = audio_stream_get_size (& sinkb -> stream );
863-
8641019 md -> mpd .in_buff_size = cd -> source_period_bytes ;
8651020 md -> mpd .out_buff_size = cd -> sink_period_bytes ;
8661021
@@ -891,6 +1046,11 @@ static int selector_prepare(struct processing_module *mod,
8911046 return - EINVAL ;
8921047 }
8931048
1049+ if (cd -> new_config ) {
1050+ cd -> new_config = false;
1051+ return selector_find_coefficients (mod );
1052+ }
1053+
8941054 return 0 ;
8951055}
8961056
@@ -908,7 +1068,9 @@ static int selector_reset(struct processing_module *mod)
9081068 cd -> source_period_bytes = 0 ;
9091069 cd -> sink_period_bytes = 0 ;
9101070 cd -> sel_func = NULL ;
911-
1071+ cd -> num_configs = 0 ;
1072+ cd -> passthrough = false;
1073+ cd -> new_config = false;
9121074 return 0 ;
9131075}
9141076
0 commit comments