diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c
index 193dc3e76d49..1ca3b76306b1 100644
--- a/src/pcm/pcm_direct.c
+++ b/src/pcm/pcm_direct.c
@@ -560,8 +560,11 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
 	return 0;
 }
 
+#define RECOVERIES_FLAG_SUSPENDED	(1U << 31)
+#define RECOVERIES_MASK			((1U << 31) - 1)
+
 /*
- * Recover slave on XRUN.
+ * Recover slave on XRUN or SUSPENDED.
  * Even if direct plugins disable xrun detection, there might be an xrun
  * raised directly by some drivers.
  * The first client recovers slave pcm.
@@ -569,6 +572,8 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
  */
 int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
 {
+	unsigned int recoveries;
+	int state;
 	int ret;
 	int semerr;
 
@@ -579,7 +584,8 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
 		return semerr;
 	}
 
-	if (snd_pcm_state(direct->spcm) != SND_PCM_STATE_XRUN) {
+	state = snd_pcm_state(direct->spcm);
+	if (state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SUSPENDED) {
 		/* ignore... someone else already did recovery */
 		semerr = snd_pcm_direct_semaphore_up(direct,
 						     DIRECT_IPC_SEM_CLIENT);
@@ -590,6 +596,24 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
 		return 0;
 	}
 
+	recoveries = direct->shmptr->s.recoveries;
+	recoveries = (recoveries + 1) & RECOVERIES_MASK;
+	if (state == SND_PCM_STATE_SUSPENDED)
+		recoveries |= RECOVERIES_FLAG_SUSPENDED;
+	direct->shmptr->s.recoveries = recoveries;
+
+	/* some buggy drivers require the device resumed before prepared;
+	 * when a device has RESUME flag and is in SUSPENDED state, resume
+	 * here but immediately drop to bring it to a sane active state.
+	 */
+	if (state == SND_PCM_STATE_SUSPENDED &&
+	    (direct->spcm->info & SND_PCM_INFO_RESUME)) {
+		snd_pcm_resume(direct->spcm);
+		snd_pcm_drop(direct->spcm);
+		snd_pcm_direct_timer_stop(direct);
+		snd_pcm_direct_clear_timer_queue(direct);
+	}
+
 	ret = snd_pcm_prepare(direct->spcm);
 	if (ret < 0) {
 		SNDERR("recover: unable to prepare slave");
@@ -621,7 +645,6 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
 		}
 		return ret;
 	}
-	direct->shmptr->s.recoveries++;
 	semerr = snd_pcm_direct_semaphore_up(direct,
 						 DIRECT_IPC_SEM_CLIENT);
 	if (semerr < 0) {
@@ -632,11 +655,15 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
 }
 
 /*
- * enter xrun state, if slave xrun occurred
- * @return: 0 for no xrun or a negative error code for xrun
+ * enter xrun or suspended state, if slave xrun occurred or suspended
+ * @return: 0 for no xrun/suspend or a negative error code for xrun/suspend
  */
 int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
 {
+	if (direct->state == SND_PCM_STATE_XRUN)
+		return -EPIPE;
+	else if (direct->state == SND_PCM_STATE_SUSPENDED)
+		return -ESTRPIPE;
 	if (direct->shmptr->s.recoveries != direct->recoveries) {
 		/* no matter how many xruns we missed -
 		 * so don't increment but just update to actual counter
@@ -649,10 +676,14 @@ int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
 		 * if slave already entered xrun again the event is lost.
 		 * snd_pcm_direct_clear_timer_queue(direct);
 		 */
-		direct->state = SND_PCM_STATE_XRUN;
+		if (direct->recoveries & RECOVERIES_FLAG_SUSPENDED) {
+			direct->state = SND_PCM_STATE_SUSPENDED;
+			return -ESTRPIPE;
+		} else {
+			direct->state = SND_PCM_STATE_XRUN;
+			return -EPIPE;
+		}
 	}
-	if (direct->state == SND_PCM_STATE_XRUN)
-		return -EPIPE;
 	return 0;
 }
 
@@ -721,13 +752,13 @@ timer_changed:
 	}
 	switch (snd_pcm_state(dmix->spcm)) {
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		/* recover slave and update client state to xrun
 		 * before returning POLLERR
 		 */
 		snd_pcm_direct_slave_recover(dmix);
 		snd_pcm_direct_client_chk_xrun(dmix, pcm);
 		/* fallthrough */
-	case SND_PCM_STATE_SUSPENDED:
 	case SND_PCM_STATE_SETUP:
 		events |= POLLERR;
 		break;
@@ -1074,27 +1105,10 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm)
 int snd_pcm_direct_resume(snd_pcm_t *pcm)
 {
 	snd_pcm_direct_t *dmix = pcm->private_data;
-	snd_pcm_t *spcm = dmix->spcm;
+	int err;
 
-	snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
-	/* some buggy drivers require the device resumed before prepared;
-	 * when a device has RESUME flag and is in SUSPENDED state, resume
-	 * here but immediately drop to bring it to a sane active state.
-	 */
-	if ((spcm->info & SND_PCM_INFO_RESUME) &&
-	    snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) {
-		snd_pcm_resume(spcm);
-		snd_pcm_drop(spcm);
-		snd_pcm_direct_timer_stop(dmix);
-		snd_pcm_direct_clear_timer_queue(dmix);
-		snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0,
-				      spcm->channels, spcm->buffer_size,
-				      spcm->format);
-		snd_pcm_prepare(spcm);
-		snd_pcm_start(spcm);
-	}
-	snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
-	return -ENOSYS;
+	err = snd_pcm_direct_slave_recover(dmix);
+	return err < 0 ? err : -ENOSYS;
 }
 
 #define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field)
diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c
index dfff18b992c5..e3bf49269f0c 100644
--- a/src/pcm/pcm_dmix.c
+++ b/src/pcm/pcm_dmix.c
@@ -431,6 +431,7 @@ static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
 		dmix->state = SND_PCM_STATE_DISCONNECTED;
 		return -ENODEV;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
 			return err;
 		break;
@@ -457,11 +458,11 @@ static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
 	snd_pcm_state_t state;
 	state = snd_pcm_state(dmix->spcm);
 	switch (state) {
-	case SND_PCM_STATE_SUSPENDED:
 	case SND_PCM_STATE_DISCONNECTED:
 		dmix->state = state;
 		return state;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
 			return err;
 		break;
@@ -833,11 +834,10 @@ static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
 
 	switch (snd_pcm_state(dmix->spcm)) {
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
 			return err;
 		break;
-	case SND_PCM_STATE_SUSPENDED:
-		return -ESTRPIPE;
 	default:
 		break;
 	}
diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c
index d63c86bd4044..5a52ae9173be 100644
--- a/src/pcm/pcm_dshare.c
+++ b/src/pcm/pcm_dshare.c
@@ -206,6 +206,7 @@ static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)
 		dshare->state = SNDRV_PCM_STATE_DISCONNECTED;
 		return -ENODEV;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
 			return err;
 		break;
@@ -260,11 +261,11 @@ static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
 	snd_pcm_state_t state;
 	state = snd_pcm_state(dshare->spcm);
 	switch (state) {
-	case SND_PCM_STATE_SUSPENDED:
 	case SND_PCM_STATE_DISCONNECTED:
 		dshare->state = state;
 		return state;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
 			return err;
 		break;
@@ -532,11 +533,10 @@ static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
 
 	switch (snd_pcm_state(dshare->spcm)) {
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
 			return err;
 		break;
-	case SND_PCM_STATE_SUSPENDED:
-		return -ESTRPIPE;
 	default:
 		break;
 	}
diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c
index 77ffbada931f..1653f6fba86c 100644
--- a/src/pcm/pcm_dsnoop.c
+++ b/src/pcm/pcm_dsnoop.c
@@ -139,6 +139,7 @@ static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
 		dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
 		return -ENODEV;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
 			return err;
 		break;
@@ -211,11 +212,11 @@ static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
 	snd_pcm_state_t state;
 	state = snd_pcm_state(dsnoop->spcm);
 	switch (state) {
-	case SND_PCM_STATE_SUSPENDED:
 	case SND_PCM_STATE_DISCONNECTED:
 		dsnoop->state = state;
 		return state;
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
 			return err;
 		break;
@@ -423,11 +424,10 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
 
 	switch (snd_pcm_state(dsnoop->spcm)) {
 	case SND_PCM_STATE_XRUN:
+	case SND_PCM_STATE_SUSPENDED:
 		if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
 			return err;
 		break;
-	case SND_PCM_STATE_SUSPENDED:
-		return -ESTRPIPE;
 	default:
 		break;
 	}
