* the update-IRQ timing.  The IRQ is issued before actually the
  * data is processed.  So, we need to process it afterwords in a
  * workqueue.
+ *
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
  */
 static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
 {
        struct snd_pcm_substream *substream = azx_dev->core.substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        int stream = substream->stream;
        u32 wallclk;
        unsigned int pos;
+       snd_pcm_uframes_t hwptr, target;
 
        wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
        if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
                /* NG - it's below the first next period boundary */
                return chip->bdl_pos_adj ? 0 : -1;
        azx_dev->core.start_wallclk += wallclk;
+
+       if (azx_dev->core.no_period_wakeup)
+               return 1; /* OK, no need to check period boundary */
+
+       if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt)
+               return 1; /* OK, already in hwptr updating process */
+
+       /* check whether the period gets really elapsed */
+       pos = bytes_to_frames(runtime, pos);
+       hwptr = runtime->hw_ptr_base + pos;
+       if (hwptr < runtime->status->hw_ptr)
+               hwptr += runtime->buffer_size;
+       target = runtime->hw_ptr_interrupt + runtime->period_size;
+       if (hwptr < target) {
+               /* too early wakeup, process it later */
+               return chip->bdl_pos_adj ? 0 : -1;
+       }
+
        return 1; /* OK, it's fine */
 }
 
        if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                return azx_skl_get_dpib_pos(chip, azx_dev);
 
-       /* For capture, we need to read posbuf, but it requires a delay
-        * for the possible boundary overlap; the read of DPIB fetches the
-        * actual posbuf
-        */
-       udelay(20);
+       /* read of DPIB fetches the actual posbuf */
        azx_skl_get_dpib_pos(chip, azx_dev);
        return azx_get_pos_posbuf(chip, azx_dev);
 }