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,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+ 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 ) {
798+ if (cd -> multi_coeffs_config_size < data_offset_size ) {
799+ /* Configuration exist but the allocation is too
800+ * small to write over.
801+ */
802+ mod_free (mod , cd -> multi_coeffs_config );
803+ cd -> multi_coeffs_config_size = data_offset_size ;
804+ cd -> multi_coeffs_config =
805+ mod_alloc (mod , cd -> multi_coeffs_config_size );
806+ }
807+ } else {
808+ /* No existing configuration */
809+ cd -> multi_coeffs_config_size = data_offset_size ;
810+ cd -> multi_coeffs_config = mod_alloc (mod , cd -> multi_coeffs_config_size );
811+ }
812+
813+ if (!cd -> multi_coeffs_config ) {
814+ comp_err (dev , "Failed to allocate configuration blob." );
815+ return - ENOMEM ;
816+ }
817+
818+ /* Copy the configuration and notify for need to re-configure */
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,111 @@ 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+ config = cd -> multi_coeffs_config ;
890+ if (cd -> num_configs > 1 ) {
891+ config = selector_config_array_search (cd , source_channels , sink_channels );
892+ /* If not found, check if pass-through mix is defined for the max
893+ * channels count (8).
894+ */
895+ if (!config && source_channels == sink_channels )
896+ config = selector_config_array_search (cd , SEL_SOURCE_CHANNELS_MAX ,
897+ SEL_SINK_CHANNELS_MAX );
898+
899+ if (!config ) {
900+ comp_err (dev , "No mix coefficients found for %d to %d channels." ,
901+ source_channels , sink_channels );
902+ return - EINVAL ;
903+ }
904+ }
905+
906+ ret = memcpy_s (& cd -> coeffs_config , sizeof (struct ipc4_selector_coeffs_config ),
907+ config , sizeof (* config ));
908+ if (ret )
909+ return ret ;
910+ }
911+
912+ /* The pass-through copy function can be used if coefficients are a unit matrix for
913+ * 1:1 stream copy.
914+ */
915+ if (source_channels == sink_channels ) {
916+ cd -> passthrough = true;
917+ for (i = 0 ; i < sink_channels ; i ++ ) {
918+ for (j = 0 ; j < source_channels ; j ++ ) {
919+ coef = cd -> coeffs_config .coeffs [i ][j ];
920+ if ((i == j && coef != SEL_COEF_ONE_Q10 ) || (i != j && coef != 0 )) {
921+ cd -> passthrough = false;
922+ break ;
923+ }
924+ }
925+ }
926+ } else {
927+ cd -> passthrough = false;
928+ }
929+
930+ if (cd -> passthrough )
931+ comp_info (dev , "Passthrough mode." );
932+ else
933+ comp_info (dev , "Using coefficients for %d to %d channels." ,
934+ source_channels , sink_channels );
935+
936+ return 0 ;
937+ }
938+
791939/**
792940 * \brief Copies and processes stream data.
793941 * \param[in,out] mod Selector base module device.
@@ -799,11 +947,29 @@ static int selector_process(struct processing_module *mod,
799947 struct output_stream_buffer * output_buffers ,
800948 int num_output_buffers )
801949{
950+ struct audio_stream * source ;
951+ struct audio_stream * sink ;
802952 struct comp_data * cd = module_get_private_data (mod );
803953 uint32_t avail_frames = input_buffers [0 ].size ;
954+ int ret ;
804955
805956 comp_dbg (mod -> dev , "entry" );
806957
958+ if (cd -> new_config ) {
959+ cd -> new_config = false;
960+ ret = selector_find_coefficients (mod );
961+ if (ret )
962+ return ret ;
963+ }
964+
965+ if (cd -> passthrough ) {
966+ source = input_buffers -> data ;
967+ sink = output_buffers -> data ;
968+ audio_stream_copy (source , 0 , sink , 0 , avail_frames * cd -> config .in_channels_count );
969+ module_update_buffer_position (input_buffers , output_buffers , avail_frames );
970+ return 0 ;
971+ }
972+
807973 if (avail_frames )
808974 /* copy selected channels from in to out */
809975 cd -> sel_func (mod , input_buffers , output_buffers , avail_frames );
@@ -850,17 +1016,7 @@ static int selector_prepare(struct processing_module *mod,
8501016 /* get sink data format and period bytes */
8511017 cd -> sink_format = audio_stream_get_frm_fmt (& sinkb -> stream );
8521018 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-
8621019 sink_size = audio_stream_get_size (& sinkb -> stream );
863-
8641020 md -> mpd .in_buff_size = cd -> source_period_bytes ;
8651021 md -> mpd .out_buff_size = cd -> sink_period_bytes ;
8661022
@@ -891,6 +1047,11 @@ static int selector_prepare(struct processing_module *mod,
8911047 return - EINVAL ;
8921048 }
8931049
1050+ if (cd -> new_config ) {
1051+ cd -> new_config = false;
1052+ return selector_find_coefficients (mod );
1053+ }
1054+
8941055 return 0 ;
8951056}
8961057
@@ -908,7 +1069,9 @@ static int selector_reset(struct processing_module *mod)
9081069 cd -> source_period_bytes = 0 ;
9091070 cd -> sink_period_bytes = 0 ;
9101071 cd -> sel_func = NULL ;
911-
1072+ cd -> num_configs = 0 ;
1073+ cd -> passthrough = false;
1074+ cd -> new_config = false;
9121075 return 0 ;
9131076}
9141077
0 commit comments