diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 887abf70da3ba1..df97a86452b48a 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -12,7 +12,7 @@ snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o endif ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) snd-sof-y += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\ - ipc4-mtrace.o ipc4-telemetry.o + ipc4-mtrace.o ipc4-telemetry.o ipc4-wov.o snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc4-compress.o endif diff --git a/sound/soc/sof/ipc4-wov.c b/sound/soc/sof/ipc4-wov.c new file mode 100644 index 00000000000000..99273dc448c23a --- /dev/null +++ b/sound/soc/sof/ipc4-wov.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2026 Intel Corporation + +#include +#include +#include +#include "sof-audio.h" +#include "ipc4-wov.h" + +/* IPC4 PHRASE_DETECTED primary/extension field layout */ +#define SOF_IPC4_PHRASE_WORD_ID_MASK GENMASK(31, 24) +#define SOF_IPC4_PHRASE_WORD_ID_SHIFT 24 +#define SOF_IPC4_PHRASE_SV_SCORE_MASK GENMASK(15, 0) + +/* PCM ID for WoV keyword detection capture stream */ +#define SOF_WOV_PCM_ID 11 + +/* kcontrol names as defined in the dmic-wov feature topology */ +#define SOF_WOV_KEYWORD_ID_CTL "wov_trigger_id" +#define SOF_WOV_EVENT_CTL "wov_event" + +/* + * kcontrol list for topology-defined WoV event/keyword-ID + * controls and trigger a user-space notification. + */ +static void sof_ipc4_wov_notify_kcontrols(struct snd_sof_dev *sdev) +{ + static const char * const wov_ctl_names[] = { + SOF_WOV_KEYWORD_ID_CTL, + SOF_WOV_EVENT_CTL, + }; + struct snd_sof_control *scontrol; + struct snd_kcontrol *kc; + int i; + + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + for (i = 0; i < ARRAY_SIZE(wov_ctl_names); i++) { + if (strcmp(scontrol->name, wov_ctl_names[i])) + continue; + /* force firmware re-read on next .get */ + scontrol->comp_data_dirty = true; + kc = snd_soc_card_get_kcontrol(scontrol->scomp->card, + scontrol->name); + if (kc) + snd_ctl_notify_one(scontrol->scomp->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, + kc, 0); + } + } +} + +void sof_ipc4_wov_phrase_detected(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct snd_sof_pcm *spcm; + struct snd_pcm_substream *substream; + u32 word_id = (ipc4_msg->primary & SOF_IPC4_PHRASE_WORD_ID_MASK) + >> SOF_IPC4_PHRASE_WORD_ID_SHIFT; + u32 sv_score = ipc4_msg->extension & SOF_IPC4_PHRASE_SV_SCORE_MASK; + + dev_dbg(sdev->dev, "WoV: PHRASE_DETECTED word_id=%u sv_score=%u\n", + word_id, sv_score); + + pm_wakeup_event(sdev->dev, 2000); + sof_ipc4_wov_notify_kcontrols(sdev); + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->pcm.pcm_id != SOF_WOV_PCM_ID) + continue; + + substream = spcm->stream[SNDRV_PCM_STREAM_CAPTURE].substream; + if (!substream || !substream->runtime) { + dev_warn(sdev->dev, "WoV: PCM %d not open\n", + SOF_WOV_PCM_ID); + return; + } + + snd_pcm_period_elapsed(substream); + return; + } + + dev_warn(sdev->dev, "WoV: PHRASE_DETECTED but PCM %d not found\n", + SOF_WOV_PCM_ID); +} diff --git a/sound/soc/sof/ipc4-wov.h b/sound/soc/sof/ipc4-wov.h new file mode 100644 index 00000000000000..fd071136f85cf9 --- /dev/null +++ b/sound/soc/sof/ipc4-wov.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* Copyright(c) 2026 Intel Corporation */ + +#ifndef __SOF_IPC4_WOV_H +#define __SOF_IPC4_WOV_H + +#include "sof-priv.h" +#include "ipc4-priv.h" + +void sof_ipc4_wov_phrase_detected(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg); + +#endif /* __SOF_IPC4_WOV_H */ diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index bf3a122ee651e8..e6c2bb21116308 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -15,6 +15,7 @@ #include "sof-audio.h" #include "ipc4-fw-reg.h" #include "ipc4-priv.h" +#include "ipc4-wov.h" #include "ipc4-topology.h" #include "ipc4-telemetry.h" #include "ops.h" @@ -840,6 +841,9 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) case SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT: snd_sof_dsp_panic(sdev, 0, true); break; + case SOF_IPC4_NOTIFY_PHRASE_DETECTED: + sof_ipc4_wov_phrase_detected(sdev, ipc4_msg); + break; case SOF_IPC4_NOTIFY_MODULE_NOTIFICATION: data_size = sizeof(struct sof_ipc4_notify_module_data); handler_func = sof_ipc4_module_notification_handler;