Skip to content

Commit 5d64ee6

Browse files
ujfalusiplbossart
authored andcommitted
ASoC: SOF: ipc4-control: Implement control update for switch/enum controls
Implement the sof_ipc_tplg_control_ops.update function to support a control change notification from the firmware on switch or enum control types. Based on the module notification message content, look up the swidget, then the scontrol which was the source of the notification then if the message contains the changed values update the cached values. If only a notification without values received, marked the control as dirty and on next read access fetch the new values from the firmware. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
1 parent 37cf7a8 commit 5d64ee6

1 file changed

Lines changed: 179 additions & 0 deletions

File tree

sound/soc/sof/ipc4-control.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
243287
static 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 */
605783
static 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

Comments
 (0)