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,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
0 commit comments