@@ -240,6 +240,50 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,
240240 return ret ;
241241}
242242
243+ static void sof_ipc4_refresh_generic_control (struct snd_sof_control * scontrol )
244+ {
245+ struct sof_ipc4_control_data * cdata = scontrol -> ipc_control_data ;
246+ struct snd_soc_component * scomp = scontrol -> scomp ;
247+ struct sof_ipc4_control_msg_payload * data ;
248+ struct sof_ipc4_msg * msg = & cdata -> msg ;
249+ size_t data_size ;
250+ unsigned int i ;
251+ int ret ;
252+
253+ if (!scontrol -> comp_data_dirty )
254+ return ;
255+
256+ if (!pm_runtime_active (scomp -> dev ))
257+ return ;
258+
259+ data_size = struct_size (data , chanv , scontrol -> num_channels );
260+ data = kmalloc (data_size , GFP_KERNEL );
261+ if (!data )
262+ return ;
263+
264+ data -> id = cdata -> index ;
265+ data -> num_elems = scontrol -> num_channels ;
266+ msg -> data_ptr = data ;
267+ msg -> data_size = data_size ;
268+
269+ scontrol -> comp_data_dirty = false;
270+ ret = sof_ipc4_set_get_kcontrol_data (scontrol , false, true);
271+ msg -> data_ptr = NULL ;
272+ msg -> data_size = 0 ;
273+ if (!ret ) {
274+ for (i = 0 ; i < scontrol -> num_channels ; i ++ ) {
275+ cdata -> chanv [i ].channel = data -> chanv [i ].channel ;
276+ cdata -> chanv [i ].value = data -> chanv [i ].value ;
277+ }
278+ } else {
279+ dev_err (scomp -> dev , "Failed to read control data for %s\n" ,
280+ scontrol -> name );
281+ scontrol -> comp_data_dirty = true;
282+ }
283+
284+ kfree (data );
285+ }
286+
243287static bool sof_ipc4_switch_put (struct snd_sof_control * scontrol ,
244288 struct snd_ctl_elem_value * ucontrol )
245289{
@@ -290,6 +334,8 @@ static int sof_ipc4_switch_get(struct snd_sof_control *scontrol,
290334 struct sof_ipc4_control_data * cdata = scontrol -> ipc_control_data ;
291335 unsigned int i ;
292336
337+ sof_ipc4_refresh_generic_control (scontrol );
338+
293339 /* read back each channel */
294340 for (i = 0 ; i < scontrol -> num_channels ; i ++ )
295341 ucontrol -> value .integer .value [i ] = cdata -> chanv [i ].value ;
@@ -347,6 +393,8 @@ static int sof_ipc4_enum_get(struct snd_sof_control *scontrol,
347393 struct sof_ipc4_control_data * cdata = scontrol -> ipc_control_data ;
348394 unsigned int i ;
349395
396+ sof_ipc4_refresh_generic_control (scontrol );
397+
350398 /* read back each channel */
351399 for (i = 0 ; i < scontrol -> num_channels ; i ++ )
352400 ucontrol -> value .enumerated .item [i ] = cdata -> chanv [i ].value ;
@@ -601,6 +649,136 @@ sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
601649 return sof_ipc4_set_volume_data (sdev , swidget , scontrol , false);
602650}
603651
652+ #define PARAM_ID_FROM_EXTENSION (_ext ) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \
653+ >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT)
654+
655+ static void sof_ipc4_control_update (struct snd_sof_dev * sdev , void * ipc_message )
656+ {
657+ struct sof_ipc4_msg * ipc4_msg = ipc_message ;
658+ struct sof_ipc4_notify_module_data * ndata = ipc4_msg -> data_ptr ;
659+ struct sof_ipc4_control_msg_payload * msg_data ;
660+ struct sof_ipc4_control_data * cdata ;
661+ struct snd_soc_dapm_widget * widget ;
662+ struct snd_sof_control * scontrol ;
663+ struct snd_sof_widget * swidget ;
664+ struct snd_kcontrol * kc = NULL ;
665+ bool scontrol_found = false;
666+ u32 event_param_id ;
667+ int i , type ;
668+
669+ if (ndata -> event_data_size < sizeof (* msg_data )) {
670+ dev_err (sdev -> dev ,
671+ "%s: Invalid event data size for module %u.%u: %u\n" ,
672+ __func__ , ndata -> module_id , ndata -> instance_id ,
673+ ndata -> event_data_size );
674+ return ;
675+ }
676+
677+ event_param_id = ndata -> event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK ;
678+ switch (event_param_id ) {
679+ case SOF_IPC4_SWITCH_CONTROL_PARAM_ID :
680+ type = SND_SOC_TPLG_TYPE_MIXER ;
681+ break ;
682+ case SOF_IPC4_ENUM_CONTROL_PARAM_ID :
683+ type = SND_SOC_TPLG_TYPE_ENUM ;
684+ break ;
685+ default :
686+ dev_err (sdev -> dev ,
687+ "%s: Invalid control type for module %u.%u: %u\n" ,
688+ __func__ , ndata -> module_id , ndata -> instance_id ,
689+ event_param_id );
690+ return ;
691+ }
692+
693+ /* Find the swidget based on ndata->module_id and ndata->instance_id */
694+ swidget = sof_ipc4_find_swidget_by_ids (sdev , ndata -> module_id ,
695+ ndata -> instance_id );
696+ if (!swidget ) {
697+ dev_err (sdev -> dev , "%s: Failed to find widget for module %u.%u\n" ,
698+ __func__ , ndata -> module_id , ndata -> instance_id );
699+ return ;
700+ }
701+
702+ /* Find the scontrol which is the source of the notification */
703+ msg_data = (struct sof_ipc4_control_msg_payload * )ndata -> event_data ;
704+ list_for_each_entry (scontrol , & sdev -> kcontrol_list , list ) {
705+ if (scontrol -> comp_id == swidget -> comp_id ) {
706+ u32 local_param_id ;
707+
708+ cdata = scontrol -> ipc_control_data ;
709+ /*
710+ * The scontrol's param_id is stored in the IPC message
711+ * template's extension
712+ */
713+ local_param_id = PARAM_ID_FROM_EXTENSION (cdata -> msg .extension );
714+ if (local_param_id == event_param_id &&
715+ msg_data -> id == cdata -> index ) {
716+ scontrol_found = true;
717+ break ;
718+ }
719+ }
720+ }
721+
722+ if (!scontrol_found ) {
723+ dev_err (sdev -> dev ,
724+ "%s: Failed to find control on widget %s: %u:%u\n" ,
725+ __func__ , swidget -> widget -> name , ndata -> event_id & 0xffff ,
726+ msg_data -> id );
727+ return ;
728+ }
729+
730+ if (msg_data -> num_elems ) {
731+ /*
732+ * The message includes the updated value/data, update the
733+ * control's local cache using the received notification
734+ */
735+ for (i = 0 ; i < msg_data -> num_elems ; i ++ ) {
736+ u32 channel = msg_data -> chanv [i ].channel ;
737+
738+ if (channel >= scontrol -> num_channels ) {
739+ dev_warn (sdev -> dev ,
740+ "Invalid channel index for %s: %u\n" ,
741+ scontrol -> name , i );
742+
743+ /*
744+ * Mark the scontrol as dirty to force a refresh
745+ * on next read
746+ */
747+ scontrol -> comp_data_dirty = true;
748+ break ;
749+ }
750+
751+ cdata -> chanv [channel ].value = msg_data -> chanv [i ].value ;
752+ }
753+ } else {
754+ /*
755+ * Mark the scontrol as dirty because the value/data is changed
756+ * in firmware, forcing a refresh on next read access
757+ */
758+ scontrol -> comp_data_dirty = true;
759+ }
760+
761+ /*
762+ * Look up the ALSA kcontrol of the scontrol to be able to send a
763+ * notification to user space
764+ */
765+ widget = swidget -> widget ;
766+ for (i = 0 ; i < widget -> num_kcontrols ; i ++ ) {
767+ /* skip non matching types or non matching indexes within type */
768+ if (widget -> dobj .widget .kcontrol_type [i ] == type &&
769+ widget -> kcontrol_news [i ].index == cdata -> index ) {
770+ kc = widget -> kcontrols [i ];
771+ break ;
772+ }
773+ }
774+
775+ if (!kc )
776+ return ;
777+
778+ snd_ctl_notify_one (swidget -> scomp -> card -> snd_card ,
779+ SNDRV_CTL_EVENT_MASK_VALUE , kc , 0 );
780+ }
781+
604782/* set up all controls for the widget */
605783static int sof_ipc4_widget_kcontrol_setup (struct snd_sof_dev * sdev , struct snd_sof_widget * swidget )
606784{
@@ -674,6 +852,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
674852 .bytes_ext_put = sof_ipc4_bytes_ext_put ,
675853 .bytes_ext_get = sof_ipc4_bytes_ext_get ,
676854 .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get ,
855+ .update = sof_ipc4_control_update ,
677856 .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup ,
678857 .set_up_volume_table = sof_ipc4_set_up_volume_table ,
679858};
0 commit comments