Merge branch 'linux-3.4.57' into rel-17
[linux-2.6.git] / sound / core / pcm_lib.c
index 11446a1..4d18941 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/math64.h>
+#include <linux/export.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -128,7 +129,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
        }
 }
 
-static void pcm_debug_name(struct snd_pcm_substream *substream,
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
                           char *name, size_t len)
 {
        snprintf(name, len, "pcmC%dD%d%c:%d",
@@ -137,6 +139,8 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
                 substream->stream ? 'c' : 'p',
                 substream->number);
 }
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
 
 #define XRUN_DEBUG_BASIC       (1<<0)
 #define XRUN_DEBUG_STACK       (1<<1)  /* dump also stack */
@@ -168,7 +172,7 @@ static void xrun(struct snd_pcm_substream *substream)
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
        if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
                char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
+               snd_pcm_debug_name(substream, name, sizeof(name));
                snd_printd(KERN_DEBUG "XRUN: %s\n", name);
                dump_stack_on_xrun(substream);
        }
@@ -189,6 +193,7 @@ static void xrun(struct snd_pcm_substream *substream)
 #define XRUN_LOG_CNT   10
 
 struct hwptr_log_entry {
+       unsigned int in_interrupt;
        unsigned long jiffies;
        snd_pcm_uframes_t pos;
        snd_pcm_uframes_t period_size;
@@ -204,7 +209,7 @@ struct snd_pcm_hwptr_log {
 };
 
 static void xrun_log(struct snd_pcm_substream *substream,
-                    snd_pcm_uframes_t pos)
+                    snd_pcm_uframes_t pos, int in_interrupt)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
@@ -220,6 +225,7 @@ static void xrun_log(struct snd_pcm_substream *substream,
                        return;
        }
        entry = &log->entries[log->idx];
+       entry->in_interrupt = in_interrupt;
        entry->jiffies = jiffies;
        entry->pos = pos;
        entry->period_size = runtime->period_size;
@@ -241,14 +247,16 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
                return;
        if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
                return;
-       pcm_debug_name(substream, name, sizeof(name));
+       snd_pcm_debug_name(substream, name, sizeof(name));
        for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
                entry = &log->entries[idx];
                if (entry->period_size == 0)
                        break;
-               snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
+               snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
                           "hwptr=%ld/%ld\n",
-                          name, entry->jiffies, (unsigned long)entry->pos,
+                          name, entry->in_interrupt ? "[Q] " : "",
+                          entry->jiffies,
+                          (unsigned long)entry->pos,
                           (unsigned long)entry->period_size,
                           (unsigned long)entry->buffer_size,
                           (unsigned long)entry->old_hw_ptr,
@@ -262,7 +270,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
 #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
 
 #define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos)       do { } while (0)
+#define xrun_log(substream, pos, in_interrupt) do { } while (0)
 #define xrun_log_show(substream)       do { } while (0)
 
 #endif
@@ -315,7 +323,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        if (pos >= runtime->buffer_size) {
                if (printk_ratelimit()) {
                        char name[16];
-                       pcm_debug_name(substream, name, sizeof(name));
+                       snd_pcm_debug_name(substream, name, sizeof(name));
                        xrun_log_show(substream);
                        snd_printd(KERN_ERR  "BUG: %s, pos = %ld, "
                                   "buffer size = %ld, period size = %ld\n",
@@ -326,7 +334,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        }
        pos -= pos % runtime->min_align;
        if (xrun_debug(substream, XRUN_DEBUG_LOG))
-               xrun_log(substream, pos);
+               xrun_log(substream, pos, in_interrupt);
        hw_base = runtime->hw_ptr_base;
        new_hw_ptr = hw_base + pos;
        if (in_interrupt) {
@@ -360,7 +368,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        if (xrun_debug(substream, in_interrupt ?
                        XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
                char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
+               snd_pcm_debug_name(substream, name, sizeof(name));
                snd_printd("%s_update: %s: pos=%u/%u/%u, "
                           "hwptr=%ld/%ld/%ld/%ld\n",
                           in_interrupt ? "period" : "hwptr",
@@ -373,6 +381,29 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                           (unsigned long)new_hw_ptr,
                           (unsigned long)runtime->hw_ptr_base);
        }
+
+       if (runtime->no_period_wakeup) {
+               snd_pcm_sframes_t xrun_threshold;
+               /*
+                * Without regular period interrupts, we have to check
+                * the elapsed time to detect xruns.
+                */
+               jdelta = jiffies - runtime->hw_ptr_jiffies;
+               if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+                       goto no_delta_check;
+               hdelta = jdelta - delta * HZ / runtime->rate;
+               xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
+               while (hdelta > xrun_threshold) {
+                       delta += runtime->buffer_size;
+                       hw_base += runtime->buffer_size;
+                       if (hw_base >= runtime->boundary)
+                               hw_base = 0;
+                       new_hw_ptr = hw_base + pos;
+                       hdelta -= runtime->hw_ptr_buffer_jiffies;
+               }
+               goto no_delta_check;
+       }
+
        /* something must be really wrong */
        if (delta >= runtime->buffer_size + runtime->period_size) {
                hw_ptr_error(substream,
@@ -442,6 +473,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                             (long)old_hw_ptr);
        }
 
+ no_delta_check:
        if (runtime->status->hw_ptr == new_hw_ptr)
                return 0;
 
@@ -997,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i,
  *
  * Returns non-zero if the value is changed, zero if not changed.
  */
-int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
+int snd_interval_list(struct snd_interval *i, unsigned int count,
+                     const unsigned int *list, unsigned int mask)
 {
         unsigned int k;
        struct snd_interval list_range;
@@ -1368,6 +1401,32 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
 
 EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
 
+static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
+                                          struct snd_pcm_hw_rule *rule)
+{
+       unsigned int base_rate = (unsigned int)(uintptr_t)rule->private;
+       struct snd_interval *rate;
+
+       rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       return snd_interval_list(rate, 1, &base_rate, 0);
+}
+
+/**
+ * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
+ * @runtime: PCM runtime instance
+ * @base_rate: the rate at which the hardware does not resample
+ */
+int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
+                              unsigned int base_rate)
+{
+       return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
+                                  SNDRV_PCM_HW_PARAM_RATE,
+                                  snd_pcm_hw_rule_noresample_func,
+                                  (void *)(uintptr_t)base_rate,
+                                  SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
+
 static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
                                  snd_pcm_hw_param_t var)
 {
@@ -1728,19 +1787,48 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
        wait_queue_t wait;
        int err = 0;
        snd_pcm_uframes_t avail = 0;
-       long tout;
+       long wait_time, tout;
 
        init_waitqueue_entry(&wait, current);
+       set_current_state(TASK_INTERRUPTIBLE);
        add_wait_queue(&runtime->tsleep, &wait);
+
+       if (runtime->no_period_wakeup)
+               wait_time = MAX_SCHEDULE_TIMEOUT;
+       else {
+               wait_time = 10;
+               if (runtime->rate) {
+                       long t = runtime->period_size * 2 / runtime->rate;
+                       wait_time = max(t, wait_time);
+               }
+               wait_time = msecs_to_jiffies(wait_time * 1000);
+       }
+
        for (;;) {
                if (signal_pending(current)) {
                        err = -ERESTARTSYS;
                        break;
                }
-               set_current_state(TASK_INTERRUPTIBLE);
+
+               /*
+                * We need to check if space became available already
+                * (and thus the wakeup happened already) first to close
+                * the race of space already having become available.
+                * This check must happen after been added to the waitqueue
+                * and having current state be INTERRUPTIBLE.
+                */
+               if (is_playback)
+                       avail = snd_pcm_playback_avail(runtime);
+               else
+                       avail = snd_pcm_capture_avail(runtime);
+               if (avail >= runtime->twake)
+                       break;
                snd_pcm_stream_unlock_irq(substream);
-               tout = schedule_timeout(msecs_to_jiffies(10000));
+
+               tout = schedule_timeout(wait_time);
+
                snd_pcm_stream_lock_irq(substream);
+               set_current_state(TASK_INTERRUPTIBLE);
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_SUSPENDED:
                        err = -ESTRPIPE;
@@ -1766,14 +1854,9 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
                        err = -EIO;
                        break;
                }
-               if (is_playback)
-                       avail = snd_pcm_playback_avail(runtime);
-               else
-                       avail = snd_pcm_capture_avail(runtime);
-               if (avail >= runtime->twake)
-                       break;
        }
  _endloop:
+       set_current_state(TASK_RUNNING);
        remove_wait_queue(&runtime->tsleep, &wait);
        *availp = avail;
        return err;