sound: pcm_lib: fix unsorted list constraint handling
[pandora-kernel.git] / sound / core / pcm_lib.c
index d659995..d8a7bb2 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/math64.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -126,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
 }
 
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define xrun_debug(substream)  ((substream)->pstr->xrun_debug)
+#define xrun_debug(substream, mask)    ((substream)->pstr->xrun_debug & (mask))
 #else
-#define xrun_debug(substream)  0
+#define xrun_debug(substream, mask)    0
 #endif
 
-#define dump_stack_on_xrun(substream) do {     \
-               if (xrun_debug(substream) > 1)  \
-                       dump_stack();           \
+#define dump_stack_on_xrun(substream) do {             \
+               if (xrun_debug(substream, 2))           \
+                       dump_stack();                   \
        } while (0)
 
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+                          char *name, size_t len)
+{
+       snprintf(name, len, "pcmC%dD%d%c:%d",
+                substream->pcm->card->number,
+                substream->pcm->device,
+                substream->stream ? 'c' : 'p',
+                substream->number);
+}
+
 static void xrun(struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-       if (xrun_debug(substream)) {
-               snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
-                          substream->pcm->card->number,
-                          substream->pcm->device,
-                          substream->stream ? 'c' : 'p');
+       if (xrun_debug(substream, 1)) {
+               char name[16];
+               pcm_debug_name(substream, name, sizeof(name));
+               snd_printd(KERN_DEBUG "XRUN: %s\n", name);
                dump_stack_on_xrun(substream);
        }
 }
@@ -154,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
 {
        snd_pcm_uframes_t pos;
 
-       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
-               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        pos = substream->ops->pointer(substream);
        if (pos == SNDRV_PCM_POS_XRUN)
                return pos; /* XRUN */
        if (pos >= runtime->buffer_size) {
                if (printk_ratelimit()) {
-                       snd_printd(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, "
+                       char name[16];
+                       pcm_debug_name(substream, name, sizeof(name));
+                       snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "
                                   "buffer size = 0x%lx, period size = 0x%lx\n",
-                                  substream->stream, pos, runtime->buffer_size,
+                                  name, pos, runtime->buffer_size,
                                   runtime->period_size);
                }
                pos = 0;
@@ -197,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
 
 #define hw_ptr_error(substream, fmt, args...)                          \
        do {                                                            \
-               if (xrun_debug(substream)) {                            \
+               if (xrun_debug(substream, 1)) {                         \
                        if (printk_ratelimit()) {                       \
                                snd_printd("PCM: " fmt, ##args);        \
                        }                                               \
@@ -251,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
        }
 
        /* Do jiffies check only in xrun_debug mode */
-       if (!xrun_debug(substream))
+       if (!xrun_debug(substream, 4))
                goto no_jiffies_check;
 
        /* Skip the jiffies check for hardwares with BATCH flag.
@@ -261,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
        if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
                goto no_jiffies_check;
        hdelta = new_hw_ptr - old_hw_ptr;
+       if (hdelta < runtime->delay)
+               goto no_jiffies_check;
+       hdelta -= runtime->delay;
        jdelta = jiffies - runtime->hw_ptr_jiffies;
        if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
                delta = jdelta /
@@ -294,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
                hw_ptr_interrupt =
                        new_hw_ptr - new_hw_ptr % runtime->period_size;
        }
+       runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
+       if (runtime->status->hw_ptr == new_hw_ptr)
+               return 0;
+
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
-       runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
        return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -342,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
                new_hw_ptr = hw_base + pos;
        }
        /* Do jiffies check only in xrun_debug mode */
-       if (xrun_debug(substream) &&
-           ((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
+       if (!xrun_debug(substream, 4))
+               goto no_jiffies_check;
+       if (delta < runtime->delay)
+               goto no_jiffies_check;
+       delta -= runtime->delay;
+       if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
                hw_ptr_error(substream,
                             "hw_ptr skipping! "
                             "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
@@ -352,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
                             ((delta * HZ) / runtime->rate));
                return 0;
        }
+ no_jiffies_check:
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
+       if (runtime->status->hw_ptr == new_hw_ptr)
+               return 0;
+
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
        return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -452,7 +485,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
                *r = 0;
                return UINT_MAX;
        }
-       div64_32(&n, c, r);
+       n = div_u64_rem(n, c, r);
        if (n >= UINT_MAX) {
                *r = 0;
                return UINT_MAX;
@@ -876,47 +909,24 @@ static int snd_interval_ratden(struct snd_interval *i,
 int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
 {
         unsigned int k;
-       int changed = 0;
+       struct snd_interval list_range;
 
        if (!count) {
                i->empty = 1;
                return -EINVAL;
        }
+       snd_interval_any(&list_range);
+       list_range.min = UINT_MAX;
+       list_range.max = 0;
         for (k = 0; k < count; k++) {
                if (mask && !(mask & (1 << k)))
                        continue;
-                if (i->min == list[k] && !i->openmin)
-                        goto _l1;
-                if (i->min < list[k]) {
-                        i->min = list[k];
-                       i->openmin = 0;
-                       changed = 1;
-                        goto _l1;
-                }
-        }
-        i->empty = 1;
-        return -EINVAL;
- _l1:
-        for (k = count; k-- > 0;) {
-               if (mask && !(mask & (1 << k)))
+               if (!snd_interval_test(i, list[k]))
                        continue;
-                if (i->max == list[k] && !i->openmax)
-                        goto _l2;
-                if (i->max > list[k]) {
-                        i->max = list[k];
-                       i->openmax = 0;
-                       changed = 1;
-                        goto _l2;
-                }
+               list_range.min = min(list_range.min, list[k]);
+               list_range.max = max(list_range.max, list[k]);
         }
-        i->empty = 1;
-        return -EINVAL;
- _l2:
-       if (snd_interval_checkempty(i)) {
-               i->empty = 1;
-               return -EINVAL;
-       }
-        return changed;
+       return snd_interval_refine(i, &list_range);
 }
 
 EXPORT_SYMBOL(snd_interval_list);
@@ -1524,6 +1534,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
+                                      void *arg)
+{
+       struct snd_pcm_hw_params *params = arg;
+       snd_pcm_format_t format;
+       int channels, width;
+
+       params->fifo_size = substream->runtime->hw.fifo_size;
+       if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
+               format = params_format(params);
+               channels = params_channels(params);
+               width = snd_pcm_format_physical_width(format);
+               params->fifo_size /= width * channels;
+       }
+       return 0;
+}
+
 /**
  * snd_pcm_lib_ioctl - a generic PCM ioctl callback
  * @substream: the pcm substream instance
@@ -1545,6 +1572,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
                return snd_pcm_lib_ioctl_reset(substream, arg);
        case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
                return snd_pcm_lib_ioctl_channel_info(substream, arg);
+       case SNDRV_PCM_IOCTL1_FIFO_SIZE:
+               return snd_pcm_lib_ioctl_fifo_size(substream, arg);
        }
        return -ENXIO;
 }