ALSA: pcm: playback silence - move silence variable updates to separate function
[linux-block.git] / sound / core / pcm_lib.c
index 8b6aeb8a78f7d3ac2819cda7b2781e1ec5035067..670572c9a8cc789803d80ab506991698beea2f7a 100644 (file)
 static int fill_silence_frames(struct snd_pcm_substream *substream,
                               snd_pcm_uframes_t off, snd_pcm_uframes_t frames);
 
+
+static inline void update_silence_vars(struct snd_pcm_runtime *runtime,
+                                      snd_pcm_uframes_t ptr,
+                                      snd_pcm_uframes_t new_ptr)
+{
+       snd_pcm_sframes_t delta;
+
+       delta = new_ptr - ptr;
+       if (delta == 0)
+               return;
+       if (delta < 0)
+               delta += runtime->boundary;
+       if ((snd_pcm_uframes_t)delta < runtime->silence_filled)
+               runtime->silence_filled -= delta;
+       else
+               runtime->silence_filled = 0;
+       runtime->silence_start = new_ptr;
+}
+
 /*
  * fill ring buffer with silence
  * runtime->silence_start: starting pointer to silence area
@@ -49,47 +68,36 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
        int err;
 
        if (runtime->silence_size < runtime->boundary) {
-               snd_pcm_sframes_t noise_dist, n;
+               snd_pcm_sframes_t noise_dist;
                snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
-               if (runtime->silence_start != appl_ptr) {
-                       n = appl_ptr - runtime->silence_start;
-                       if (n < 0)
-                               n += runtime->boundary;
-                       if ((snd_pcm_uframes_t)n < runtime->silence_filled)
-                               runtime->silence_filled -= n;
-                       else
-                               runtime->silence_filled = 0;
-                       runtime->silence_start = appl_ptr;
-               }
-               if (runtime->silence_filled >= runtime->buffer_size)
-                       return;
-               noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+               update_silence_vars(runtime, runtime->silence_start, appl_ptr);
+               /* initialization outside pointer updates */
+               if (new_hw_ptr == ULONG_MAX)
+                       new_hw_ptr = runtime->status->hw_ptr;
+               /* get hw_avail with the boundary crossing */
+               noise_dist = appl_ptr - new_hw_ptr;
+               if (noise_dist < 0)
+                       noise_dist += runtime->boundary;
+               /* total noise distance */
+               noise_dist += runtime->silence_filled;
                if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
                        return;
                frames = runtime->silence_threshold - noise_dist;
                if (frames > runtime->silence_size)
                        frames = runtime->silence_size;
        } else {
+               /*
+                * This filling mode aims at free-running mode (used for example by dmix),
+                * which doesn't update the application pointer.
+                */
                if (new_hw_ptr == ULONG_MAX) {  /* initialization */
                        snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
                        if (avail > runtime->buffer_size)
                                avail = runtime->buffer_size;
                        runtime->silence_filled = avail > 0 ? avail : 0;
-                       runtime->silence_start = (runtime->status->hw_ptr +
-                                                 runtime->silence_filled) %
-                                                runtime->boundary;
+                       runtime->silence_start = runtime->status->hw_ptr;
                } else {
-                       ofs = runtime->status->hw_ptr;
-                       frames = new_hw_ptr - ofs;
-                       if ((snd_pcm_sframes_t)frames < 0)
-                               frames += runtime->boundary;
-                       runtime->silence_filled -= frames;
-                       if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
-                               runtime->silence_filled = 0;
-                               runtime->silence_start = new_hw_ptr;
-                       } else {
-                               runtime->silence_start = ofs;
-                       }
+                       update_silence_vars(runtime, runtime->status->hw_ptr, new_hw_ptr);
                }
                frames = runtime->buffer_size - runtime->silence_filled;
        }
@@ -97,7 +105,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
                return;
        if (frames == 0)
                return;
-       ofs = runtime->silence_start % runtime->buffer_size;
+       ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
        while (frames > 0) {
                transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
                err = fill_silence_frames(substream, ofs, transfer);
@@ -1878,15 +1886,14 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
                if (substream->wait_time) {
                        wait_time = substream->wait_time;
                } else {
-                       wait_time = 10;
+                       wait_time = 100;
 
                        if (runtime->rate) {
-                               long t = runtime->period_size * 2 /
-                                        runtime->rate;
+                               long t = runtime->buffer_size * 1100 / runtime->rate;
                                wait_time = max(t, wait_time);
                        }
-                       wait_time = msecs_to_jiffies(wait_time * 1000);
                }
+               wait_time = msecs_to_jiffies(wait_time);
        }
 
        for (;;) {
@@ -1934,8 +1941,8 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
                }
                if (!tout) {
                        pcm_dbg(substream->pcm,
-                               "%s write error (DMA or IRQ trouble?)\n",
-                               is_playback ? "playback" : "capture");
+                               "%s timeout (DMA or IRQ trouble?)\n",
+                               is_playback ? "playback write" : "capture read");
                        err = -EIO;
                        break;
                }
@@ -2155,6 +2162,8 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
                ret = substream->ops->ack(substream);
                if (ret < 0) {
                        runtime->control->appl_ptr = old_appl_ptr;
+                       if (ret == -EPIPE)
+                               __snd_pcm_xrun(substream);
                        return ret;
                }
        }