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 )
@@ -617,8 +621,8 @@ static int selector_init(struct processing_module *mod)
617621 if (!cd )
618622 return - ENOMEM ;
619623
620- cd -> sel_ipc4_cfg .init_payload_fmt = payload_fmt ;
621624 md -> private = cd ;
625+ cd -> sel_ipc4_cfg .init_payload_fmt = payload_fmt ;
622626
623627 if (payload_fmt == IPC4_SEL_INIT_PAYLOAD_BASE_WITH_EXT ) {
624628 size_t size = sizeof (struct sof_selector_ipc4_pin_config );
@@ -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,45 @@ 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+ int n ;
772778
773779 if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID ) {
774- if (data_offset_size != sizeof (cd -> coeffs_config ))
780+ if (data_offset_size > SEL_MAX_CONFIG_BLOB_SIZE ||
781+ pos != MODULE_CFG_FRAGMENT_SINGLE ) {
782+ comp_err (mod -> dev , "Failure with size %u pos %u" , data_offset_size , pos );
775783 return - EINVAL ;
784+ }
776785
777- memcpy_s (& cd -> coeffs_config , sizeof (cd -> coeffs_config ), fragment , data_offset_size );
778- return 0 ;
786+ /* The size must be N times the coefficient vectors size of one channels
787+ * up/down mix profile.
788+ */
789+ n = data_offset_size / sizeof (struct ipc4_selector_coeffs_config );
790+ if (n < 1 || data_offset_size != n * sizeof (struct ipc4_selector_coeffs_config )) {
791+ comp_err (mod -> dev , "Invalid configuration size." );
792+ return - EINVAL ;
793+ }
794+
795+ cd -> num_configs = n ;
796+ if (cd -> multi_coeffs_config ) {
797+ if (cd -> multi_coeffs_config_size < data_offset_size ) {
798+ /* Configuration exist but the allocation is too
799+ * small to write over.
800+ */
801+ mod_free (mod , cd -> multi_coeffs_config );
802+ cd -> multi_coeffs_config_size = data_offset_size ;
803+ cd -> multi_coeffs_config =
804+ mod_alloc (mod , cd -> multi_coeffs_config_size );
805+ }
806+ } else {
807+ /* No existing configuration */
808+ cd -> multi_coeffs_config_size = data_offset_size ;
809+ cd -> multi_coeffs_config = mod_alloc (mod , cd -> multi_coeffs_config_size );
810+ }
811+
812+ /* Copy the configuration and notify for need to re-configure */
813+ cd -> new_config = true;
814+ return memcpy_s (cd -> multi_coeffs_config , cd -> multi_coeffs_config_size ,
815+ fragment , data_offset_size );
779816 }
780817
781818 return - EINVAL ;
@@ -788,6 +825,109 @@ static int selector_get_config(struct processing_module *mod, uint32_t config_id
788825 return 0 ;
789826}
790827
828+ /**
829+ * \brief Loop the array of mix coefficients sets and find a set with matching channels
830+ * in and out count.
831+ * \param[in,out] cd Selector component data.
832+ * \param[in] source_channels Number of channels in source.
833+ * \param[in] sink_channels Number of channels in sink.
834+ *
835+ * \return Boolean value set to true if match was found and cd->coeffs_config is pointed
836+ * to the coefficients.
837+ */
838+ static struct ipc4_selector_coeffs_config * selector_config_array_search (struct comp_data * cd ,
839+ int source_channels ,
840+ int sink_channels )
841+ {
842+ struct ipc4_selector_coeffs_config * found = NULL ;
843+ int i ;
844+
845+ for (i = 0 ; i < cd -> num_configs ; i ++ ) {
846+ if (cd -> multi_coeffs_config [i ].source_channels_count == source_channels &&
847+ cd -> multi_coeffs_config [i ].sink_channels_count == sink_channels ) {
848+ found = & cd -> multi_coeffs_config [i ];
849+ break ;
850+ }
851+ }
852+
853+ return found ;
854+ }
855+
856+ /**
857+ * \brief Get mix coefficients set from configuration blob with multiple coefficients sets.
858+ * Also activate more efficient pass-through copy mode if the coefficients indicate 1:1
859+ * copy from source to sink.
860+ * \param[in,out] mod Selector base module device.
861+ * \param[in] source_channels Number of channels in source.
862+ * \param[in] sink_channels Number of channels in sink.
863+ *
864+ * \return Error code.
865+ */
866+ static int selector_find_coefficients (struct processing_module * mod )
867+ {
868+ struct comp_data * cd = module_get_private_data (mod );
869+ struct comp_dev * dev = mod -> dev ;
870+ struct ipc4_selector_coeffs_config * config ;
871+ uint32_t source_channels = cd -> config .in_channels_count ;
872+ uint32_t sink_channels = cd -> config .out_channels_count ;
873+ int i , j ;
874+ int16_t coef ;
875+
876+ /* In set_config() the blob is copied to cd->multi_coeffs_config. A legacy blob contains a
877+ * single set of mix coefficients without channels information. A new blob with multiple
878+ * configurations has the source and sink channels count information. If there has been no
879+ * set_config(), then the cd->coeffs_config has been initialized in set_selector_params()
880+ * to mix with coefficients SEL_COEF_ONE_Q10 for matching input and output channels.
881+ */
882+ if (cd -> multi_coeffs_config ) {
883+ config = cd -> multi_coeffs_config ;
884+ if (cd -> num_configs > 1 ) {
885+ config = selector_config_array_search (cd , source_channels , sink_channels );
886+ /* If not found, check if pass-through mix is defined for the max
887+ * channels count (8).
888+ */
889+ if (!config && source_channels == sink_channels )
890+ config = selector_config_array_search (cd , SEL_SOURCE_CHANNELS_MAX ,
891+ SEL_SINK_CHANNELS_MAX );
892+
893+ if (!config ) {
894+ comp_err (dev , "No mix coefficients found for %d to %d channels." ,
895+ source_channels , sink_channels );
896+ return - EINVAL ;
897+ }
898+ }
899+
900+ memcpy_s (& cd -> coeffs_config , sizeof (struct ipc4_selector_coeffs_config ),
901+ config , sizeof (* config ));
902+ }
903+
904+ /* The pass-through copy function can be used if coefficients are a unit matrix for
905+ * 1:1 stream copy.
906+ */
907+ if (source_channels == sink_channels ) {
908+ cd -> passthrough = true;
909+ for (i = 0 ; i < sink_channels ; i ++ ) {
910+ for (j = 0 ; j < source_channels ; j ++ ) {
911+ coef = cd -> coeffs_config .coeffs [i ][j ];
912+ if ((i == j && coef != SEL_COEF_ONE_Q10 ) || (i != j && coef != 0 )) {
913+ cd -> passthrough = false;
914+ break ;
915+ }
916+ }
917+ }
918+ } else {
919+ cd -> passthrough = false;
920+ }
921+
922+ if (cd -> passthrough )
923+ comp_info (dev , "Passthrough mode." );
924+ else
925+ comp_info (dev , "Using coefficients for %d to %d channels." ,
926+ source_channels , sink_channels );
927+
928+ return 0 ;
929+ }
930+
791931/**
792932 * \brief Copies and processes stream data.
793933 * \param[in,out] mod Selector base module device.
@@ -799,11 +939,29 @@ static int selector_process(struct processing_module *mod,
799939 struct output_stream_buffer * output_buffers ,
800940 int num_output_buffers )
801941{
942+ struct audio_stream * source ;
943+ struct audio_stream * sink ;
802944 struct comp_data * cd = module_get_private_data (mod );
803945 uint32_t avail_frames = input_buffers [0 ].size ;
946+ int ret ;
804947
805948 comp_dbg (mod -> dev , "entry" );
806949
950+ if (cd -> new_config ) {
951+ cd -> new_config = false;
952+ ret = selector_find_coefficients (mod );
953+ if (ret )
954+ return ret ;
955+ }
956+
957+ if (cd -> passthrough ) {
958+ source = input_buffers -> data ;
959+ sink = output_buffers -> data ;
960+ audio_stream_copy (source , 0 , sink , 0 , avail_frames * cd -> config .in_channels_count );
961+ module_update_buffer_position (input_buffers , output_buffers , avail_frames );
962+ return 0 ;
963+ }
964+
807965 if (avail_frames )
808966 /* copy selected channels from in to out */
809967 cd -> sel_func (mod , input_buffers , output_buffers , avail_frames );
@@ -850,17 +1008,7 @@ static int selector_prepare(struct processing_module *mod,
8501008 /* get sink data format and period bytes */
8511009 cd -> sink_format = audio_stream_get_frm_fmt (& sinkb -> stream );
8521010 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-
8621011 sink_size = audio_stream_get_size (& sinkb -> stream );
863-
8641012 md -> mpd .in_buff_size = cd -> source_period_bytes ;
8651013 md -> mpd .out_buff_size = cd -> sink_period_bytes ;
8661014
@@ -891,6 +1039,11 @@ static int selector_prepare(struct processing_module *mod,
8911039 return - EINVAL ;
8921040 }
8931041
1042+ if (cd -> new_config ) {
1043+ cd -> new_config = false;
1044+ return selector_find_coefficients (mod );
1045+ }
1046+
8941047 return 0 ;
8951048}
8961049
@@ -908,7 +1061,9 @@ static int selector_reset(struct processing_module *mod)
9081061 cd -> source_period_bytes = 0 ;
9091062 cd -> sink_period_bytes = 0 ;
9101063 cd -> sel_func = NULL ;
911-
1064+ cd -> num_configs = 0 ;
1065+ cd -> passthrough = false;
1066+ cd -> new_config = false;
9121067 return 0 ;
9131068}
9141069
0 commit comments