Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sound/soc/sof/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
85 changes: 85 additions & 0 deletions sound/soc/sof/ipc4-wov.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// Copyright(c) 2026 Intel Corporation

#include <linux/bits.h>
#include <linux/pm_wakeup.h>
#include <sound/pcm.h>
#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);
}
13 changes: 13 additions & 0 deletions sound/soc/sof/ipc4-wov.h
Original file line number Diff line number Diff line change
@@ -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 */
4 changes: 4 additions & 0 deletions sound/soc/sof/ipc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
Loading