diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 0bf7d25434d7f..f06ef7c718733 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -102,6 +102,8 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_TRIGGER_SUSPEND	5
 #define SNDRV_PCM_TRIGGER_RESUME	6
 #define SNDRV_PCM_TRIGGER_DRAIN		7
+#define SNDRV_PCM_TRIGGER_DETECT_STOP	8
+#define SNDRV_PCM_TRIGGER_DETECT_START	9
 
 #define SNDRV_PCM_POS_XRUN		((snd_pcm_uframes_t)-1)
 
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 4cd513215bcd8..c9f53d79638be 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -313,7 +313,8 @@ typedef int __bitwise snd_pcm_state_t;
 #define	SNDRV_PCM_STATE_PAUSED		((__force snd_pcm_state_t) 6) /* stream is paused */
 #define	SNDRV_PCM_STATE_SUSPENDED	((__force snd_pcm_state_t) 7) /* hardware is suspended */
 #define	SNDRV_PCM_STATE_DISCONNECTED	((__force snd_pcm_state_t) 8) /* hardware is disconnected */
-#define	SNDRV_PCM_STATE_LAST		SNDRV_PCM_STATE_DISCONNECTED
+#define	SNDRV_PCM_STATE_DETECTING	((__force snd_pcm_state_t) 9) /* stream is detecting (e.g. voice detection) */
+#define	SNDRV_PCM_STATE_LAST		SNDRV_PCM_STATE_DETECTING
 
 enum {
 	SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000,
@@ -703,6 +704,7 @@ enum {
 #define SNDRV_PCM_IOCTL_READI_FRAMES	_IOR('A', 0x51, struct snd_xferi)
 #define SNDRV_PCM_IOCTL_WRITEN_FRAMES	_IOW('A', 0x52, struct snd_xfern)
 #define SNDRV_PCM_IOCTL_READN_FRAMES	_IOR('A', 0x53, struct snd_xfern)
+#define SNDRV_PCM_IOCTL_DETECT		_IO('A', 0x54)
 #define SNDRV_PCM_IOCTL_LINK		_IOW('A', 0x60, int)
 #define SNDRV_PCM_IOCTL_UNLINK		_IO('A', 0x61)
 
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b465fb6e1f5f0..4fe1d2f41149c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1410,6 +1410,72 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
 	return res;
 }
 
+/*
+ * detection callbacks
+ */
+
+static int snd_pcm_pre_detect(struct snd_pcm_substream *substream,
+			      snd_pcm_state_t state)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+		return -EBADFD;
+	/* Detection only makes sense on capture stream */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -EINVAL;
+	runtime->trigger_tstamp_latched = false;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static int snd_pcm_do_detect(struct snd_pcm_substream *substream,
+			     snd_pcm_state_t state)
+{
+	if (substream->runtime->trigger_master != substream)
+		return 0;
+	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_DETECT_START);
+}
+
+static void snd_pcm_undo_detect(struct snd_pcm_substream *substream,
+				snd_pcm_state_t state)
+{
+	if (substream->runtime->trigger_master == substream)
+		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_DETECT_STOP);
+}
+
+static void snd_pcm_post_detect(struct snd_pcm_substream *substream,
+				snd_pcm_state_t state)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_pcm_trigger_tstamp(substream);
+	runtime->hw_ptr_jiffies = jiffies;
+	runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / runtime->rate;
+	runtime->status->state = state;
+	snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
+}
+
+static const struct action_ops snd_pcm_action_detect = {
+	.pre_action = snd_pcm_pre_detect,
+	.do_action = snd_pcm_do_detect,
+	.undo_action = snd_pcm_undo_detect,
+	.post_action = snd_pcm_post_detect,
+};
+
+/**
+ * snd_pcm_detect - start all detection streams
+ * @substream: the PCM substream instance
+ *
+ * Return: Zero if successful, or a negative error code.
+ * The stream lock must be acquired before calling this function.
+ */
+int snd_pcm_detect(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_action_lock_irq(&snd_pcm_action_detect, substream,
+				       SNDRV_PCM_STATE_DETECTING);
+}
+
 /*
  * start callbacks
  */
@@ -1417,7 +1483,8 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream,
 			     snd_pcm_state_t state)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	if (runtime->state != SNDRV_PCM_STATE_PREPARED)
+	if (runtime->state != SNDRV_PCM_STATE_PREPARED &&
+	    runtime->state != SNDRV_PCM_STATE_DETECTING)
 		return -EBADFD;
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 	    !snd_pcm_playback_data(substream))
@@ -2919,10 +2986,14 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
 static int do_pcm_hwsync(struct snd_pcm_substream *substream)
 {
 	switch (substream->runtime->state) {
+	case SNDRV_PCM_STATE_DETECTING:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			return -EBADFD;
+		return snd_pcm_update_hw_ptr(substream);
 	case SNDRV_PCM_STATE_DRAINING:
 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 			return -EBADFD;
-		fallthrough;
+		return snd_pcm_update_hw_ptr(substream);
 	case SNDRV_PCM_STATE_RUNNING:
 		return snd_pcm_update_hw_ptr(substream);
 	case SNDRV_PCM_STATE_PREPARED:
@@ -3342,6 +3413,8 @@ static int snd_pcm_common_ioctl(struct file *file,
 		return snd_pcm_prepare(substream, file);
 	case SNDRV_PCM_IOCTL_RESET:
 		return snd_pcm_reset(substream);
+	case SNDRV_PCM_IOCTL_DETECT:
+		return snd_pcm_detect(substream);
 	case SNDRV_PCM_IOCTL_START:
 		return snd_pcm_start_lock_irq(substream);
 	case SNDRV_PCM_IOCTL_LINK:
